Runtime secrets are key-value pairs the worker reads at request time; they are
not packed into the bundle. Set them with wdl secret put; the worker reads
them through env.<KEY>.
Do not put secrets in [vars] — [vars] values are part of the bundle and
visible to anyone with read access.
- API keys, tokens, signing keys — anything that must not appear in a git diff or build output.
Put non-sensitive configuration (greeting strings, feature flags, public URLs)
in [vars] in wrangler.json / wrangler.jsonc / wrangler.toml.
# Worker-level (most common). Promotes a new version; new traffic cold-loads the updated value.
printf '%s' "$VAL" | wdl secret put --worker <worker-name> KEY
# Namespace-level (shared). Takes effect at the next natural cold-load — it does
# **not** bump every worker. Use sparingly, only when the value really should be
# shared by every worker in the namespace.
printf '%s' "$VAL" | wdl secret put --scope ns KEYUse printf '%s' (not echo) to avoid a trailing newline at the end of the
secret value.
wdl secret list --worker <worker-name>
wdl secret list --scope ns
wdl secret delete --worker <worker-name> KEY
wdl secret delete --scope ns KEYWhen automation needs the raw control response, list / put / delete all
accept --json. wdl secret delete prompts for confirmation by default. Run
wdl secret list first to make sure you have the right key; do not add
--yes on your own.
worker-level secret > namespace-level secret > [vars]
For a duplicate key, the worker-level secret overrides the namespace-level one.
A same-named [vars] entry is shadowed by both kinds of secret.
Changing a worker-level secret creates and promotes a new version, but already-loaded historical versions can keep holding the old value until runtime eviction or recycle. When strict revocation matters, also consider disabling the old credential.
Worker-level secret mutations are atomic: if the active version changes during
the update, control returns secret_mutation_contention and the CLI asks you to
retry instead of leaving a stored-but-not-promoted partial update. Namespace
secret mutations can similarly return namespace_secret_mutation_contention
when retained worker metadata keeps changing.
If a secret mutation returns secret_encryption_unconfigured,
secret_decrypt_failed, invalid_envelope, unsupported_envelope,
unknown_kid, or secret_not_encrypted, the mutation was not written. These
are operator-side secret-envelope configuration or stored-data repair problems;
retry after the operator reports the envelope issue repaired.
- Keys must follow environment-variable grammar:
[A-Za-z_][A-Za-z0-9_]*, with a maximum length of 128 characters. Runtime-reserved names and reservedObject.prototypekeys are rejected. - Values are limited to 64 KiB.
- Secrets count toward the workerLoader env budget together with
[vars]and binding metadata. If a mutation returnsworker_env_too_large, reduce the env payload or redeploy/delete the retained version named in the error.
export default {
async fetch(request, env) {
const stripeKey = env.STRIPE_KEY; // worker-level or ns-level
// ...
},
};- ❌
[vars] = { STRIPE_KEY = "sk_live_..." }.[vars]goes into the bundle. Usewdl secret put. - ❌ Hardcoding third-party API tokens in
.envor Wrangler config. Push them withwdl secret put. - ❌ Adding
--yestowdl secret deletewithout runningwdl secret listfirst and confirming with the user. - ❌ Using
echo "$VAL" |instead ofprintf '%s' "$VAL" |.echoappends a newline, which gets written into the secret value. - ❌ Expecting a namespace-level secret to take effect on every worker immediately. It does not — it takes effect at the next cold-load. For "effective now", use a worker-level secret.
- deploy.md —
ADMIN_TOKEN(the deploy credential, not a runtime secret).