n4nAI

Cloudflare Workers environment secrets for provider API keys

Hands-on tutorial: store and access Cloudflare Workers secrets for provider API keys when integrating LLMs at the edge, with runnable code.

n4n Team4 min read840 words

Audio narration

Coming soon — every post will get a voice note here.

Shipping LLM features on Cloudflare Workers means you need a clean way to handle cloudflare workers secrets api keys without leaking them to the client or committing them to git. This guide walks through the exact steps to store provider credentials as encrypted secrets, read them in your Worker, and call an LLM provider securely from the edge.

Step 1: Scaffold a Worker for LLM calls

Start with a minimal Worker project. If you don’t have one, use wrangler to init:

npm create cloudflare@latest llm-edge-worker
cd llm-edge-worker
npm install

Pick the “Hello World” Worker (TypeScript) template. The generated src/index.ts exports a fetch handler. That handler is where we’ll read secrets and proxy requests to an LLM provider. Keep the logic thin: parse the incoming request, call the provider using the secret, return the streamed response.

A common mistake is bundling the provider SDK. Most LLM providers expose an OpenAI-compatible REST endpoint, so a plain fetch avoids bloat and keeps cold starts low. If you need streaming, return the upstream ReadableStream directly instead of buffering the whole response.

Project layout

You should end up with:

src/index.ts
wrangler.toml
.dev.vars (gitignored)

The wrangler.toml holds routes and compatibility flags, not keys. Keep it that way.

Step 2: Store provider keys as encrypted secrets

Never paste keys into wrangler.toml or source files. Cloudflare encrypts secrets at rest and injects them as environment variables at runtime. Use the CLI:

wrangler secret put OPENAI_API_KEY
wrangler secret put ANTHROPIC_API_KEY

The command prompts for the value. For production, scope it:

wrangler secret put OPENAI_API_KEY --env production

You can manage cloudflare workers secrets api keys for each provider this way. List what’s stored with:

wrangler secret list

Secrets are write-only; you cannot retrieve the value after setting it. If you lose it, delete and put a new one. Values are capped at 1 KB, which is plenty for bearer tokens. For local development, create a .dev.vars file (gitignored) with the same names—wrangler dev reads it automatically.

CI injection

In pipelines, pipe the value in:

echo "$OPENAI_API_KEY" | wrangler secret put OPENAI_API_KEY --env production

This avoids interactive prompts and keeps the key out of shell history if you use a masked CI variable.

Step 3: Read secrets inside the Worker handler

Define an Env interface and reference env.KEY. Here’s a minimal handler that calls OpenAI’s chat completions:

interface Env {
  OPENAI_API_KEY: string;
}

export default {
  async fetch(req: Request, env: Env): Promise<Response> {
    const payload = {
      model: "gpt-4o-mini",
      messages: [{ role: "user", content: "Say hi." }],
      stream: true,
    };

    const upstream = await fetch("https://api.openai.com/v1/chat/completions", {
      method: "POST",
      headers: {
        "content-type": "application/json",
        authorization: `Bearer ${env.OPENAI_API_KEY}`,
      },
      body: JSON.stringify(payload),
    });

    return new Response(upstream.body, {
      headers: { "content-type": "text/event-stream" },
      status: upstream.status,
    });
  },
};

The secret never touches the response. If you need multiple providers, add fields to Env and branch on the request path or header. Secrets are per-environment, so env in staging differs from production.

Step 4: Keep keys out of logs and error responses

It’s tempting to log env during debugging. Don’t. Cloudflare streams Worker logs to the dashboard; a single console.log(env) exposes your cloudflare workers secrets api keys to anyone with log access. Wrap upstream calls in try/catch and return generic errors:

try {
  const res = await fetch(/* ... */);
  if (!res.ok) return new Response("Provider error", { status: 502 });
  return res;
} catch (e) {
  return new Response("Upstream failed", { status: 503 });
}

Also strip authorization headers from any incoming request before forwarding—clients must not send their own provider keys through your Worker. If you need to pass a user token to a gateway, use a separate signed header and validate it before mapping to the secret.

Step 5: Handle multiple providers and fallback

When you run several cloudflare workers secrets api keys, you’ll want fallback logic if one provider rate-limits. A naive approach:

const providers = [
  { url: "https://api.openai.com/v1/chat/completions", key: env.OPENAI_API_KEY },
  { url: "https://api.anthropic.com/v1/messages", key: env.ANTHROPIC_API_KEY },
];
for (const p of providers) {
  const r = await fetch(p.url, {
    method: "POST",
    headers: { authorization: `Bearer ${p.key}`, "content-type": "application/json" },
    body: req.body,
  });
  if (r.status !== 429 && r.status !== 503) return r;
}
return new Response("All providers down", { status: 503 });

If you’d rather not juggle a dozen cloudflare workers secrets api keys for each LLM vendor, a gateway such as n4n.ai consolidates them behind one OpenAI-compatible endpoint that addresses 240+ models and applies automatic fallback when a provider is degraded. You’d then store a single N4N_API_KEY secret and forward requests with provider routing hints. That reduces secret sprawl and per-provider quota handling.

Cache-control hints

Providers support prompt caching via headers. Forward cache-control from the client only if you trust it; otherwise set your own x-cache-ttl based on the route. The gateway or provider honors those hints, but your Worker should sanitize them.

Step 6: Test locally with .dev.vars

Create .dev.vars in the project root:

OPENAI_API_KEY=sk-test-123
ANTHROPIC_API_KEY=sk-ant-test-456

Run wrangler dev. The Worker reads these as env.OPENAI_API_KEY. Confirm with a curl to http://localhost:8787. Never commit .dev.vars—add it to .gitignore. For CI, inject secrets via the platform’s secret store, not the file.

Local verification

curl -X POST http://localhost:8787 -H "content-type: application/json" \
  -d '{"prompt":"hello"}' --verbose 2>&1 | grep -i "authorization"

You should see the header sent upstream in the verbose output, but the key value must match your .dev.vars, not anything from the client.

Step 7: Deploy and verify success

Deploy:

wrangler deploy --env production

Verification should prove two things: the Worker can authenticate, and secrets aren’t leaked. Hit the endpoint:

curl -s https://your-worker.workers.dev/ -H "content-type: application/json" \
  -d '{"prompt":"ping"}' | head -c 200

Expect a valid model response. Then check wrangler tail and confirm no authorization header or key string appears in logs. Finally, run a negative test: temporarily delete a secret and confirm the Worker returns your generic 502, not a stack trace with the key.

Automated check

Add a smoke test in CI that deploys to a preview environment and asserts the response status is 200 and the body does not contain sk-. This catches accidental logging regressions.

Step 8: Rotate and audit secrets

Providers leak. Rotate quarterly or on suspicion. Delete and re-put:

wrangler secret delete OPENAI_API_KEY
wrangler secret put OPENAI_API_KEY --env production

Audit with wrangler secret list per environment. If you used the gateway approach, rotation is a single key. For direct providers, keep an inventory of which cloudflare workers secrets api keys map to which Worker and environment.

Secrets in Cloudflare Workers are immutable per deploy; a put triggers a new deployment. That’s the right trade-off—no hot-reload of credentials, full version traceability. Treat each secret as a revocable capability, and your edge LLM integration stays clean.

Tagscloudflare-workerssecretssecurityapi-keys

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All cloudflare workers llm integration posts →