Cloudflare Workers secrets are a solid production secret store. If you’re deploying Workers, they’re the right call: encrypted at rest, injected at runtime, never readable after creation, and deeply integrated with the Cloudflare platform.
They are not designed for local development. And they’re not designed to protect you when an AI coding agent is running commands on your machine.
This distinction matters more now that tools like Claude Code are a normal part of how developers write and test Workers locally. This post covers where Cloudflare secrets hold, where they don’t, and how to handle the local development gap.
What Cloudflare Workers secrets actually do
When you add a secret to a Worker with wrangler secret put, it’s encrypted and stored by Cloudflare. The secret is injected into the Worker runtime as an environment binding — it’s accessible inside your Worker code as env.SECRET_NAME, but it never appears in Cloudflare’s dashboard, it can’t be retrieved via the API, and it can’t be pulled back to your local machine after it’s set.
This is good security design for a hosted execution environment. Cloudflare has access to the keys (they’re holding the secret on your behalf), but the write-only constraint means the surface area for accidental exposure is low. Once it’s in, the value doesn’t come back out.
The Cloudflare Secrets Store, currently in open beta, extends this to cross-Worker secret sharing — one stored credential can be bound to multiple Workers without duplication.
What Cloudflare Workers secrets don’t do
They don’t protect your local development environment.
When you run Workers locally with wrangler dev, your local environment reads from .dev.vars — a plaintext dotenv file in your project root. This is the local equivalent of Workers secrets. It’s explicitly not a secure store; it’s designed to be a .env stand-in for local iteration.
# .dev.vars — plaintext, never commit thisSECRET_API_KEY=sk-live-xxxxxxxxxxxxDATABASE_URL=postgresql://user:pass@localhost:5432/mydbWrangler’s documentation is clear about this: .dev.vars should be in your .gitignore. But it doesn’t address what happens when an AI coding agent is operating in your project directory.
They don’t prevent an AI agent from reading your local secrets.
Claude Code is a filesystem and shell agent. When you’re iterating on a Worker locally, Claude Code may open .dev.vars during debugging, project exploration, or when asked to understand your environment setup. When it does, the values enter Claude’s context window.
There’s no wrangler-level mechanism that prevents this. The production secret store (Cloudflare’s servers) is isolated from your local environment by design. That isolation stops at the boundary of your machine.
They don’t give you subprocess-level injection for local commands.
If Claude Code runs a command that needs a credential — a database migration, an API call to verify a key, a test against a third-party service — the credential has to come from somewhere. If it comes from .dev.vars (read by Claude and passed to the command), it went through Claude’s context. If it comes from wrangler dev’s environment injection, that’s subprocess-inherited environment — Claude Code can still read it if it runs env or printenv.
The local development gap
The gap is specific: you need secrets accessible to your Worker code during local development, but you don’t want Claude Code to hold the plaintext values while helping you write, debug, and test that code.
Cloudflare’s tooling doesn’t address this. wrangler dev is not an AI-agent-aware runtime. The .dev.vars mechanism predates the era of filesystem agents.
Three tools exist that sync credentials from a central store to your local environment:
Doppler supports Cloudflare Workers via a manual CLI integration — you sync secrets to your Worker’s environment using Wrangler and a Doppler Service Token. Locally, you run doppler run -- wrangler dev, which injects credentials into the subprocess environment before it starts. This keeps credentials out of .dev.vars, but doppler run sets environment variables before spawning the subprocess — a Claude Code session that runs commands inherits that environment and can inspect it.
Infisical has a first-party Cloudflare Workers sync integration for production, and supports infisical run for local wrapping. Same limitation: the agent inherits the environment.
1Password CLI (op run) can inject credentials as environment variables into any subprocess, including wrangler dev. Same pattern, same limitation.
All three tools solve the “secrets in .dev.vars” problem correctly. None of them solve the “AI agent holds plaintext in environment” problem — because they weren’t designed for it. Token management, secret scoping, and process injection semantics differ between these tools — they are not drop-in equivalents.
Two separate layers, two separate concerns
It helps to think of Cloudflare Workers secrets and local AI development secrets as two different problems at two different layers:
| Layer | Tool | Concern addressed |
|---|---|---|
| Cloudflare production runtime | Workers secrets / Secrets Store | Secret injection into deployed Workers |
| Local Cloudflare sync | Doppler, Infisical, 1Password CLI | Sync from central store to .dev.vars or wrangler dev env |
| Local AI agent development | OpaqueVault | Per-subprocess secret injection, Claude never holds plaintext |
OpaqueVault is not a Cloudflare Workers secret store. It doesn’t deploy secrets to your Worker or sync with Cloudflare’s platform. What it does is fill the gap between “I have credentials my Worker code needs locally” and “I don’t want Claude Code to see those values while I’m building.”
When Claude Code needs to run a command that requires a credential — say, testing your Worker against a live Stripe webhook, running a database migration, or verifying a third-party API key — vault_run decrypts the credential locally and injects it into that subprocess’s environment. Claude sees the output, not the key.
A combined setup for Cloudflare Workers development might look like:
# Local development secrets (never in .dev.vars)ov secret set STRIPE_SECRET_KEYov secret set DATABASE_URL
# Production secrets go in Cloudflare (via wrangler or Doppler sync)wrangler secret put STRIPE_SECRET_KEY # prompted, write-only.dev.vars contains only non-sensitive configuration:
# .dev.vars — safe to have Claude read thisENVIRONMENT=developmentLOG_LEVEL=debugWhen Claude Code runs commands during development:
Claude → vault_run(["node", "scripts/test-stripe-webhook.js"], ["STRIPE_SECRET_KEY"]) → OpaqueVault decrypts STRIPE_SECRET_KEY locally → Spawns subprocess with key in env → Returns test output to Claude → Claude sees results, not the keyWhen you deploy, your production secrets are in Cloudflare Workers secrets. The two layers don’t overlap — they’re complementary.
What about the Cloudflare Secrets Store?
The Cloudflare Secrets Store (open beta) allows one secret to be bound across multiple Workers. The security model is the same as Workers secrets: Cloudflare manages the keys, write-only after creation, injected at Worker runtime. It doesn’t change the local development situation.
Practical recommendations
For production Workers: Use Cloudflare Workers secrets. They’re the right tool — encrypted, write-only after creation, injected at runtime, with no local exposure. If you’re managing many credentials across multiple Workers, evaluate the Secrets Store for centralization.
For syncing credentials to local environments: Doppler (doppler run) and Infisical (infisical run) are common options. They keep secrets out of .dev.vars and provide central management. Understand that secrets injected this way are in the subprocess environment — inherited by any child process, including those spawned by Claude Code.
For Claude Code sessions: Use OpaqueVault. Store the same credentials you’d otherwise put in .dev.vars, and use vault_run when Claude needs to execute authenticated commands. This is the only approach that prevents Claude from holding the plaintext value while still allowing it to run commands on your behalf.
These three layers address different threat surfaces. They’re not alternatives to each other — a well-configured Cloudflare Workers project uses all three.
OpaqueVault is a zero-knowledge MCP secret manager built for AI coding agents. Secrets are encrypted on your machine before leaving it, and the remote server stores only ciphertext it cannot decrypt. Get started free →
Related: How to Store Secrets for Claude Code · Stop Telling Claude to Read Your .env File
Corrections (April 2026):
- Confirmed Cloudflare Secrets Store status as open beta (as of April 2026). (Cloudflare Secrets Store docs)
- Noted that Doppler and Infisical CLI tools differ from
op runin token management and secret scoping — not drop-in equivalents.