A cat lying across a keyboard as if to stop work

Local .dev.vars vs production secrets for Astro on Workers


Every time I return to a Cloudflare Workers project I relearn the same rule: git is not a secrets store. Local dev still needs real keys for honest testing.

Files and what they are for

FileCommitted?Used by
.env.example / .dev.vars.exampleYesDocuments variable names
.dev.varsNoWrangler bindings during astro dev / astro preview
.envNoNASA_API_KEY for fetch-apod; optional copies of other keys
Cloudflare dashboardN/AProduction Worker vars and secrets

Copy the examples once:

cp .env.example .env
cp .dev.vars.example .dev.vars

Fill in .env: NASA_API_KEY (build-time APOD fetch). .env.example documents Turnstile keys—copy those into .dev.vars as well so /contact and /api/contact see them during astro dev. Add RESEND_API_KEY, CONTACT_FROM_EMAIL, and CONTACT_TO_EMAIL to both .dev.vars and .env if you want local mail to send (names are in astro.config.mjs but not in the committed examples). Never commit the copies.

What to test locally

  1. npm run dev — page renders; Turnstile widget appears when the site key is set in .dev.vars.
  2. Submit the form — POST reaches /api/contact and returns JSON, not HTML.

In dev, missing Turnstile secrets skip the production 503 gate (import.meta.env.DEV), and the submit button stays enabled without a site key—so local behavior is looser than production. Use astro preview (or npm run preview) when you need routing closer to a built Worker.

Production parity

A variable in .dev.vars does not exist in production until you create it in the dashboard too. I treat Worker secrets as part of the deploy checklist, same as DNS.

If env changes are not picked up, restart dev and clear .astro—see Fix Astro dev port conflicts.

Next: Astro secrets on Cloudflare Workers for the readEnv helper and Fix Astro API route 404 when something still fails in production.


Hero photo: You shall not work, CC BY-SA 2.0.