Overview
Secrets are encrypted environment variables for sensitive data like API keys, tokens, and credentials. Unlike plain environment variables, secrets:
Are encrypted at rest
Never appear in logs or error messages
Can only be set, not read via API
Are bound to your Worker at runtime
Creating Secrets
Interactive Mode
Create a secret with interactive prompt:
wrangler secret put API_KEY
You’ll be prompted to enter the secret value (input is hidden):
Enter a secret value: ********
🌀 Creating the secret for the Worker "my-worker"
✨ Success! Uploaded secret API_KEY
From stdin
Pipe secret values from other commands:
echo "my-secret-value" | wrangler secret put API_KEY
# From a file
cat api-key.txt | wrangler secret put API_KEY
# From password manager
pass show api-key | wrangler secret put API_KEY
Bulk Secret Upload
Upload multiple secrets at once:
wrangler secret bulk secrets.json
Create secrets.json:
{
"API_KEY" : "abc123" ,
"DATABASE_URL" : "postgres://..." ,
"STRIPE_KEY" : "sk_test_..."
}
Then upload:
wrangler secret bulk secrets.json
Alternatively, use .env format:
API_KEY = abc123
DATABASE_URL = postgres://...
STRIPE_KEY = sk_test_...
wrangler secret bulk .env.production
From stdin
cat secrets.json | wrangler secret bulk
Bulk Upload Implementation
The bulk command validates and uploads secrets efficiently:
// From secret/index.ts:582-635
export async function parseBulkInputToObject (
input ?: string
) : Promise < BulkInputResult | undefined > {
let content : Record < string , string >;
let secretSource : "file" | "stdin" ;
let secretFormat : "json" | "dotenv" ;
if ( input ) {
secretSource = "file" ;
const jsonFilePath = path . resolve ( input );
try {
const fileContent = readFileSync ( jsonFilePath );
try {
content = parseJSON ( fileContent ) as Record < string , string >;
secretFormat = "json" ;
} catch ( e ) {
content = dotenvParse ( fileContent );
secretFormat = "dotenv" ;
}
} catch ( e ) {
throw new FatalError (
`The contents of " ${ input } " is not valid JSON: " ${ e } "`
);
}
validateFileSecrets ( content , input );
}
return { content , secretSource , secretFormat };
}
Listing Secrets
View all secrets for a Worker:
Pretty format (default):
Secret Name: API_KEY
Secret Name: DATABASE_URL
Secret Name: STRIPE_KEY
JSON format:
wrangler secret list --format json
[
{
"name" : "API_KEY"
},
{
"name" : "DATABASE_URL"
},
{
"name" : "STRIPE_KEY"
}
]
Secret values cannot be retrieved via CLI or API. You can only see secret names, not their values.
Deleting Secrets
Remove a secret:
wrangler secret delete API_KEY
You’ll be asked to confirm:
? Are you sure you want to permanently delete the secret API_KEY on the Worker my-worker ? ( y/N )
🌀 Deleting the secret API_KEY on the Worker my-worker
✨ Success! Deleted secret API_KEY
Secrets with Versions
When using Worker versions, secrets require special handling to avoid accidental deployments.
Version Secret Commands
Use the version-specific commands:
# Create/update secret (creates new version)
wrangler versions secret put API_KEY
# List secrets in deployed versions
wrangler versions secret list
# Delete secret (creates new version)
wrangler versions secret delete API_KEY
Creating Version with Secret
When you add a secret using wrangler versions secret put, it:
Prompts for secret value
Enter a secret value: ********
Creates new version
The command copies the latest version with the new secret: // From versions/secrets/put.ts:73-98
const versions = await fetchResult (
config ,
`/accounts/ ${ accountId } /workers/scripts/ ${ scriptName } /versions`
). items ;
const latestVersion = versions [ 0 ];
const newVersion = await copyWorkerVersionWithNewSecrets ({
config ,
accountId ,
scriptName ,
versionId: latestVersion . id ,
secrets: [{ name: args . key , value: secretValue }],
versionMessage: args . message ?? `Updated secret " ${ args . key } "` ,
versionTag: args . tag ,
});
Returns new version ID
✨ Success! Created version a1b2c3d4 with secret API_KEY.
➡️ To deploy this version to production traffic use the command "wrangler versions deploy".
Version Secret List
List secrets in currently deployed versions:
wrangler versions secret list
Output:
-- Version a1b2c3d4-e5f6-7890-abcd-ef1234567890 (80%) secrets --
Secret Name: API_KEY
Secret Name: DATABASE_URL
-- Version b2c3d4e5-f6a7-8901-bcde-f12345678901 (20%) secrets --
Secret Name: API_KEY
Secret Name: OLD_DATABASE_URL
This shows:
Which secrets exist in each deployed version
Traffic percentage for each version
Secret name differences between versions
Latest Version Only
wrangler versions secret list --latest-version
-- Version a1b2c3d4 (0%) secrets --
Secret Name: API_KEY
Secret Name: DATABASE_URL
Secret Name: NEW_FEATURE_KEY
Shows secrets in the most recently uploaded version (even if not deployed).
Standard vs. Version Secrets
Standard Deployment Secrets
wrangler secret put API_KEY
✅ Simple single command
✅ Immediate deployment
❌ May trigger unwanted deployment
❌ No gradual rollout
Use when: Using standard deployments without versions.
Version Deployment Secrets
wrangler versions secret put API_KEY
# Then: wrangler versions deploy
✅ No accidental deployment
✅ Can test before deploying
✅ Gradual rollout support
❌ Two-step process
Use when: Using gradual rollouts with versions.
Secret Deployment Behavior
Standard Deployment
With standard deployments, wrangler secret put may trigger a deployment:
// From secret/index.ts:184-197
try {
return await fetchResult ( config , url , {
method: "PUT" ,
headers: { "Content-Type" : "application/json" },
body: JSON . stringify ({
name: args . key ,
text: secretValue ,
type: "secret_text" ,
}),
});
} catch ( e ) {
if ( e instanceof APIError && e . code === VERSION_NOT_DEPLOYED_ERR_CODE ) {
throw new UserError (
"Secret edit failed. You attempted to modify a secret, but the latest version isn't currently deployed."
);
}
}
With standard deployments, adding a secret when the latest version isn’t deployed will fail. You must either:
Deploy the latest version first, then add secrets
Use wrangler versions secret put instead
Version Deployment
With version deployments:
Secrets create a new version
No automatic deployment
Explicit wrangler versions deploy required
Accessing Secrets in Code
Secrets are available as environment variables:
export default {
async fetch ( request , env ) {
// Access secret from env
const apiKey = env . API_KEY ;
// Use in API calls
const response = await fetch ( 'https://api.example.com' , {
headers: {
'Authorization' : `Bearer ${ apiKey } `
}
});
return response ;
}
}
TypeScript Types
Define environment types:
interface Env {
API_KEY : string ;
DATABASE_URL : string ;
STRIPE_KEY : string ;
}
export default {
async fetch ( request : Request , env : Env ) : Promise < Response > {
// Now env.API_KEY is typed
const key = env . API_KEY ;
// ...
}
}
Secret Inheritance
When uploading a new version, secrets are inherited from the previous version by default:
// From versions/upload.ts:710-711
keepVars : props . keepVars ?? false ,
keepSecrets : true , // secrets are always inherited
This means:
New versions automatically include existing secrets
You only need to update changed secrets
Deleted secrets require explicit removal
Secret Security
Best Practices
Never commit secrets - Use .gitignore for secret files
Rotate regularly - Update secrets periodically
Use least privilege - Only grant necessary permissions
Audit secret access - Monitor which Workers use which secrets
Delete unused secrets - Remove secrets no longer needed
Secret Storage
Secrets are:
✅ Encrypted at rest
✅ Encrypted in transit
✅ Never logged
✅ Never returned by API
✅ Isolated per Worker
What NOT to Use Secrets For
❌ Configuration values - Use environment variables instead
❌ Public API keys - Use plain environment variables
❌ Non-sensitive data - Use environment variables or KV
❌ Large data - Use KV, R2, or D1
Environment-Specific Secrets
For Workers with environments:
# Production secrets
wrangler secret put API_KEY --env production
# Staging secrets
wrangler secret put API_KEY --env staging
# Development secrets
wrangler secret put API_KEY --env dev
Each environment has isolated secrets.
Troubleshooting
Worker Not Found
If the Worker doesn’t exist:
❌ Worker "my-worker" not found.
Solution: The CLI will offer to create a draft Worker:
? There doesn 't seem to be a Worker called "my-worker".
Do you want to create a new Worker with that name and add secrets to it? (Y/n)
Latest Version Not Deployed
When using versions:
❌ Secret edit failed. You attempted to modify a secret, but the latest
version of your Worker isn't currently deployed.
Solution: Use version-specific commands:
wrangler versions secret put API_KEY
Bulk Upload Failures
If bulk upload fails:
❌ The contents of "secrets.json" is not valid JSON
Solution: Validate JSON format or use .env format instead.
Next Steps
Deploying Deploy Workers with secrets
Versioning Manage Worker versions