Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cloudflare/workers-sdk/llms.txt

Use this file to discover all available pages before exploring further.

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

JSON Format

Create secrets.json:
{
  "API_KEY": "abc123",
  "DATABASE_URL": "postgres://...",
  "STRIPE_KEY": "sk_test_..."
}
Then upload:
wrangler secret bulk secrets.json

Environment File Format

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:
wrangler secret list

Output Formats

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:
1

Prompts for secret value

Enter a secret value: ********
2

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,
});
3

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:
  1. Deploy the latest version first, then add secrets
  2. 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

  1. Never commit secrets - Use .gitignore for secret files
  2. Rotate regularly - Update secrets periodically
  3. Use least privilege - Only grant necessary permissions
  4. Audit secret access - Monitor which Workers use which secrets
  5. 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

Build docs developers (and LLMs) love