List every variable the app needs, by name and by environment
Write the names (not values) in a checked-in file such as .env.example, with a column for build time, runtime and browser. Compare it against the platform's variable list for Production and Preview whenever you change platform, branch strategy or runtime.
Validate required configuration at startup and refuse to boot
Throw on a missing or empty value instead of defaulting. In Next.js, the register function runs code on server startup, which is a good place for this check.
const required = ["DATABASE_URL", "SITE_URL", "STRIPE_SECRET_KEY"];
const missing = required.filter((k) => !process.env[k]);
if (missing.length) {
throw new Error(`Missing config: ${missing.join(", ")}`);
}
Remove silent fallbacks for anything environment-specific
Hosts, redirect URLs, allowed origins, database names and keys get no default. A default is fine for values that are the same everywhere, such as a page size.
Set build-time values before the build, then rebuild
For NEXT_PUBLIC_ and VITE_ variables, set the value for the right environment first, then trigger a new build. Redeploying an old build, or promoting one built elsewhere, keeps the old value. If one build must serve several environments, read server-only values at request time, not at module load.
Check scope and environment for each variable
On Vercel, confirm the variable is ticked for Production, Preview or both, and redeploy after any change. On Netlify, confirm the Functions scope and the deploy context (Production, Deploy Previews, Branch deploys) cover where the code runs.
Rotate secrets in the right order
Create the new credential, update the platform variable, redeploy every project that uses it, verify, and only then revoke the old credential. Vercel's own guidance is to update before you invalidate, because old deployments keep the old value.
Scan the built output for secrets before release
Search the client bundle for key formats and private-key headers, and fail the pipeline on a hit. Netlify does this for variables flagged as secret and fails the build when it finds one. Keep secrets out of build logs, images and ARG or ENV lines in Dockerfiles.
grep -rlE "sk_live_|sk_test_|service_role|BEGIN (RSA )?PRIVATE KEY" .next/static dist build 2>/dev/null && exit 1 || true
Send an explicit CORS origin from the deployed API
The response needs Access-Control-Allow-Origin set to your real frontend origin, and Access-Control-Allow-Credentials: true if cookies are sent. A credentialed request cannot use the * wildcard. Build the allowed origin from a required variable, not from a localhost default.
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Vary: Origin