Cloudflare Workers exposes runtime configuration as bindings on the handler's env argument. Plain vars are visible configuration, secrets are encrypted bindings, and Vite-prefixed values are build-time constants that can land in a browser bundle. Mixing those channels is how staging values get frozen into production code or secrets get committed to Wrangler configuration.
A minimal runtime variable
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "env-demo",
"main": "src/index.ts",
"compatibility_date": "2026-09-14",
"vars": { "APP_MODE": "development" }
}interface Env { APP_MODE: string }
export default {
fetch(_request: Request, env: Env): Response {
return new Response(env.APP_MODE);
},
} satisfies ExportedHandler<Env>;npx wrangler dev
curl http://localhost:8787developmentKeep the example non-secret. Add deployed secrets with wrangler secret put NAME and local secrets with an ignored .dev.vars or .env file.
Local file precedence is asymmetric
Wrangler 4 uses .dev.vars in preference to dotenv files. If it exists, Wrangler does not load .env into the Worker. An environment-specific .dev.vars.staging replaces the generic file, so repeat every required key. Dotenv variants merge instead, with the most specific file winning per key.
# .dev.vars
API_ORIGIN="https://dev.example.invalid"
FEATURE_MODE="safe"
# .dev.vars.staging must repeat FEATURE_MODE if required
API_ORIGIN="https://staging.example.invalid"
npx wrangler dev --env stagingNamed environments do not inherit vars or secrets
Wrangler's vars and secrets are non-inheritable. Define each binding in every named environment that uses it. This duplication prevents production from silently inheriting a development endpoint.
{
"vars": { "APP_MODE": "development" },
"env": {
"production": { "vars": { "APP_MODE": "production" } }
}
}Bindings, process.env, and Vite are different
Prefer typed bindings because env also exposes KV, R2, and services. process.env can be populated when Node compatibility is enabled; nodejs_compat_populate_process_env defaults on for compatibility dates from 2025-04-01. One exception matters: Wrangler and the Cloudflare Vite plugin replace process.env.NODE_ENV at build time. Client-side import.meta.env.VITE_* is also public, build-time data. See the build-time env guide.
Check dotenv syntax with the env validator or create a dotenv starting point with the env config builder.
Limitations and version scope
varsare not encrypted. Never store tokens in Wrangler configuration.- Runtime bindings cannot change JavaScript already emitted into static assets.
- Node compatibility does not make every Node.js API fully implemented.
This guide targets Wrangler 4 and Workers behavior documented on September 14, 2026. Pin Wrangler in CI and review compatibility-date changes before relying on new runtime behavior.