Environment Variables
Wisp reads these once at start. An empty value counts as unset. A non-empty invalid value stops the server with a message. Switches take on/off (also 1/0, true/false).
Browser code reads PUBLIC_* variables as env.PUBLIC_X, filled in at build. With public_env = true in [package.metadata.wisp], import { env } from 'wisp' reads them as the server saw them at its start (Config). A prerendered page (PRERENDER) that the server sends holds the server's values from its start. wisp build --static runs the server on the build machine, so the exported pages hold that machine's values.
A variable comes from the process first. Then Wisp reads these files in the app folder. A later file wins: .env, then .env.development (dev mode, WISP_DEV) or .env.production (other modes), then .env.local, then .env.development.local or .env.production.local. A file that does not exist adds nothing. Wisp reads the files once, at start. The build reads the files of its profile (release reads production) for the baked PUBLIC_* values, and the server picks its mode from WISP_DEV at start. Set WISP_DEV in the process or in .env: the mode files cannot choose the mode. The mode follows the build profile: a debug build (as under wisp dev) reads development and a release build reads production, unless WISP_DEV says otherwise. Cargo does not see a mode file that you create after the first build. Run cargo clean -p <app> and then wisp build again, or touch build.rs. Keep the .local files out of git (wisp new ignores .env.*).
For your own variables use wisp::env("KEY"), wisp::env_or("KEY", 4) or #[derive(Config)]. Call Conf::get() after the app starts, or Conf::load()? first in a test or another binary, because get() panics on bad variables.
Server
| Name | What it does |
|---|---|
HOST, PORT | listen address (0.0.0.0:3000 in release, 127.0.0.1 in dev) |
ORIGIN | the site's address as browsers see it, for a proxy that does not pass Host. A .onion address turns WISP_ONION on |
WISP_DEV | dev mode: on in debug builds. A release binary with WISP_DEV reads static/ and .wisp/ in its working directory, so run it from the app folder |
WISP_THREADS | worker threads, one per CPU |
WISP_LISTEN | unix:<path>: listen on that Unix-domain socket instead of HOST/PORT, for a proxy on the same machine (Deploy). Linux and macOS, on tokio's driver (not io_uring or epoll). A stale socket file there is replaced; a live server's socket and any other file are an error, and the file is removed on stop. WebSocket upgrades fail on it. Any other value stops the server |
WISP_IO | Linux: epoll instead of io_uring; uring fails at start where it cannot run |
WISP_MAX_CONNS | open connections before a 503 (unset: on Linux the open-file limit less 1024, within a quarter of memory at 16 KiB each; 10000 elsewhere; 0 no cap) |
WISP_BODY_LIMIT | largest request body (512KB, 10MB; 1 MB). const BODY_LIMIT sets it per route |
WISP_UPLOAD_LIMIT | most a request's Upload files may take in all (100 MB) |
WISP_SPOOL_LIMIT | most all requests together may stream to upload temp files at once, process-wide (default: 16× WISP_UPLOAD_LIMIT). Over it, a new file part is refused 503 until space frees; 0 no cap |
WISP_HANDLER_TIMEOUT | handler timeout in seconds |
WISP_WS_IDLE | seconds a quiet WebSocket lives (60; 0 never) |
WISP_SSE_HEARTBEAT | seconds between : comment lines on a quiet Response::events stream (25; 0 none) |
WISP_DRAIN | seconds a stopping server waits for requests under way (10) |
WISP_STOP_DELAY | seconds between SIGTERM and closing the listener, while /_wisp/health answers 503 (0) |
WISP_USER | uid or uid:gid: started as root, give WISP_DATA to that user and run as them. If the server cannot start itself as that user, it stops and does not run as root |
LISTEN_FDS | Linux systemd socket activation: with LISTEN_PID this process, serve on the socket at descriptor 3 instead of binding PORT |
LISTEN_PID | the process LISTEN_FDS is meant for, set by systemd |
WISP_CLIENT_IP_DEPTH | which entry of WISP_CLIENT_IP_HEADER (and of x-forwarded-proto, x-forwarded-host), counted from the right, is the client's (1). Use 2 behind Render or Cloud Run |
WISP_CLIENT_IP_HEADER | header with the client address behind a proxy. The last address of the header's last line counts, the one the trusted proxy added; a client can forge the first. Setting it also makes x-forwarded-proto: http believed (plain cookies); unset, cookies are Secure except in dev and for plain http to localhost or loopback. wisp deploy init sets it where the host's header is safe |
WISP_LAMBDA_STREAM | 1 on AWS Lambda with a Function URL in RESPONSE_STREAM mode (Streaming) |
WISP_SECURE_HEADERS | off leaves out nosniff and referrer-policy |
WISP_REFERRER | the referrer-policy pages get (default strict-origin-when-cross-origin) |
WISP_HSTS | on adds strict-transport-security, not on plain http requests |
WISP_ONION | on: serve as a Tor onion service. Automatic for a .onion ORIGIN or SITE_URL; off turns it off; auto counts only requests to a .onion host. See Tor onion services |
WISP_ONION_LOCATION | an onion address: clearnet pages answer with onion-location: http://….onion/<path> |
WISP_SERVER_TIMING | Server-Timing on every answer (on in dev) |
WISP_PROBLEM_JSON | on: errors as RFC 9457 problem JSON |
WISP_REQUEST_ID | on: every request gets an id (cx.request_id()), echoed as x-request-id |
WISP_ALLOWED_HOSTS | comma-separated Host values (127.0.0.1:1420,localhost:1420, with the port the browser sends; on 80 or 443 list it without the port, as browsers leave it out). A request with another Host, or with none (HTTP/1.0), is a 421 (Misdirected Request), so a page on another name pointed at this address (DNS rebinding) cannot read the app. Unset: any host, and nothing is checked. Not derived from ORIGIN, since a proxy may rewrite Host. wisp::allow_hosts sets it from code (Deploy targets) |
WISP_API_DOCS | serve /_wisp/openapi.json and /_wisp/docs (on in dev; no docs page on Cloudflare, Pages, Vercel, Netlify) |
WISP_SECRET, WISP_SECRET_OLD | key for signed cookies and tokens, and the one before (rotation). Unset outside dev, an app that signs does not start (openssl rand -hex 32) |
WISP_DATA | folder for table files (off: in memory) |
WISP_FSYNC | second (default), always, off |
WISP_STORE | a store for tables: d1:DB, deno-kv, libsql://… |
WISP_KV_<NAME> | driver of wisp::kv("NAME"): cf:BINDING, deno-kv[:path], netlify-blobs[:store], fs[:dir], memory, upstash[:url]. Unset: the host's own store. See Storage |
WISP_BLOB_<NAME> | the same for wisp::blobs("NAME"), plus s3:<endpoint>/<bucket> and vercel-blob[:private] |
WISP_SQL_<NAME> | database of wisp::sql("NAME"): libsql://<host>, https://<host> (Turso), d1-http:<account>/<database>. Unset: the D1 binding on Cloudflare, else 501 |
WISP_KV_<NAME>_TOKEN, WISP_SQL_<NAME>_TOKEN, WISP_BLOB_<NAME>_TOKEN | the token for that store's driver |
WISP_BLOB_<NAME>_KEY_ID, WISP_BLOB_<NAME>_SECRET, WISP_BLOB_<NAME>_REGION, WISP_BLOB_<NAME>_SESSION_TOKEN | the s3: driver's credentials (else AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_SESSION_TOKEN) |
UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN, KV_REST_API_URL, KV_REST_API_TOKEN | REST address and token for upstash |
BLOB_READ_WRITE_TOKEN, VERCEL_BLOB_API_URL | token and API address for vercel-blob |
CLOUDFLARE_API_TOKEN | token for d1-http: when WISP_SQL_<NAME>_TOKEN is unset |
NETLIFY_BLOBS_CONTEXT | set by Netlify, for netlify-blobs |
WISP_STORE_TOKEN | bearer token for a libsql:// store |
WISP_STORE_POLL | seconds between store change polls (several servers) |
WISP_BASE | base path, set at build (/app); same as base in [package.metadata.wisp] |
WISP_ASSET_BASE | where built /_app/ files are linked from, set at build (https://cdn.example.com or /static). A cross-origin host is added to script-src and style-src of the default CSP |
SITE_URL | site address for sitemaps, feeds and absolute links (also read from the .env files). Without it, only dev mode takes the request's Host. A release build then serves no /sitemap.xml, /robots.txt or /feed.xml, and wisp::alternates gives nothing. An edge build always uses SITE_URL alone, so set it on the edge host. |
SITE_TITLE | feed title |
METRICS_KEY | bearer key for /_wisp/metrics |
CRON_SECRET | bearer key hosts send to /_wisp/cron/<schedule> |
WISP_SERVE | 1: Node's server.mjs listens even when a launcher imports it (PM2 cluster, symlinked path) |
WISP_EXIT_ON_STDIN_END | 1: Node, Bun and Deno servers exit when stdin closes (wisp dev --target sets it) |
WISP_NODE_HTTP | 1: Node, Bun and Deno targets use the runtime's own HTTP server |
WISP_KEEP_ALIVE_TIMEOUT | Node, Bun and Deno targets: seconds a quiet kept-alive connection lives (65; Bun.serve takes at most 255) |
AWS_LAMBDA_RUNTIME_API | set by Lambda; the wisp build --target lambda binary answers its runtime API |
AWS_ENDPOINT_URL_APIGATEWAYMANAGEMENTAPI | where a Lambda's WebSocket sends go (a VPC endpoint, LocalStack, a test) |
WISP_EDITOR, EDITOR | what the dev error dialog's Open runs (code -g) |
#[derive(Rest)] bearer tokens and OAuth client ids and secrets are read from the names their attributes give.
Set by the CLI and Hosts
You do not set these.
| Name | What it does |
|---|---|
WISP_NONCE | 0 while wisp build builds wasm for an app with no wisp::csp_nonce(, leaving the nonce code out |
WISP_REQUEST_ONLY | 1 for Cloudflare, Pages, Vercel or Netlify builds: leaves the raw-connection server loop out of the wasm |
WISP_LITS | 1 in wisp dev cargo builds: string, number and bool literals are read from a table so a literal-only save swaps in with no compile (see Literal Swap) |
WISP_WARM_UP | the Cloudflare worker's warm-up instance (WISP_WARM_UP=1) |
WISP_NONCE=0: 6,747 bytes (about 6.7 KB) smaller for wisp-test-app (2,527,786 bytes without it, 2,521,039 with it, Windows host, bench/edge/README.md).
Logs and Traces
| Name | What it does |
|---|---|
WISP_LOG | json: a JSON line per request with a severity |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP/HTTP traces to <endpoint>/v1/traces |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | the traces URL, as is |
OTEL_EXPORTER_OTLP_HEADERS | api-key=…,x=… |
OTEL_EXPORTER_OTLP_TRACES_HEADERS | the same, for traces only (wins) |
OTEL_SERVICE_NAME | service name on spans |
OTEL_BSP_SCHEDULE_DELAY | batch delay in ms (5000) |
CLI and Tests
| Name | What it does |
|---|---|
SSL_CERT_FILE | wisp dev only: a PEM file of extra root certificates for the https:// targets of [package.metadata.wisp.dev.proxy], such as a local server with its own certificate (.wisp/dev-cert.pem of another wisp dev --https) |
WISP_DEV_ORIGIN | extra hosts, comma-separated, that may read wisp dev's event stream (Security) |
WISP_STATIC | 1 at build: keep the static export (WISP_EXPORT). wisp build --static sets it. A release build with no PRERENDER page leaves the prerenderer out, and WISP_PRERENDER then writes nothing |
WISP_LAMBDA | 1 at build: keep the AWS Lambda runtime. wisp build --target lambda sets it |
WISP_DEAD_LINKS | warn: wisp build (its prerender crawl) and wisp build --static only warn of dead links and missing #ids, like --allow-dead-links (Edge and serverless targets) |
WISP_NO_CRAWL | 1: wisp build (its prerender) and wisp build --static follow no link, like --no-crawl |
WISP_NO_UPDATE_CHECK | 1 silences the CLI-older-than-app warning and the daily newer-release lookup and the changes it lists (CI set does the same for the lookup) |
WISP_RELEASES_URL | the https release list URL that wisp upgrade, wisp changelog and the newer-release lookup read (default: the Forgejo API at code.wispweb.dev). Forgejo or GitHub releases JSON |
WISP_PORT_TRIES | how many next ports wisp dev tries (20) |
WISP_DEV_CARGO | any value: wisp dev builds with cargo, never rustc directly |
WISP_BUILD_CACHE | 0: off. A path: where the build cache lives. By default wisp build, dev, test and new share one cargo build dir (%LOCALAPPDATA%\wisp\build, ~/Library/Caches/wisp/build, ~/.cache/wisp/build), so a second app reuses compiled dependencies. Skipped when CARGO_TARGET_DIR, CARGO_BUILD_TARGET_DIR, CARGO_BUILD_BUILD_DIR or a cargo config target-dir/build-dir is set. On E0463, E0786, E0460, E0514 or LNK1207 it cleans that package and retries once. wisp clean --cache empties it |
WISP_DEV_DIRECT | 1: on macOS, wisp dev builds .rs and .wisp saves with rustc directly (experimental); on Linux it skips the startup proof |
WISP_DEV_PORT | set by wisp dev for [package.metadata.wisp.dev] run commands: the port it serves on (Config) |
WISP_CWEBP | the cwebp wisp build uses for images |
WISP_SASS | the Dart Sass for src/app.scss |
WISP_TSC | the tsc for wisp check --types |
WISP_BROWSER | Chrome or Edge for browser tests |
NO_COLOR | plain CLI output |
Is This Page Useful?