Validate required variables at server startup
Fail at startup with the missing variable's name, instead of letting a request crash later. In Next.js, run the check from the register function in instrumentation.ts, which runs when the server starts.
const required = ["DATABASE_URL", "STRIPE_SECRET_KEY", "NEXT_PUBLIC_SITE_URL"];
const missing = required.filter((name) => !process.env[name]);
if (missing.length > 0) {
throw new Error(`Missing environment variables: ${missing.join(", ")}`);
}
Remove mock and empty-string fallbacks
A fallback such as process.env.X ?? "" or a mock sign-in provider hides a missing variable: the app appears to work but runs on an empty or fake value. In client code, reference public variables by their full literal name so the bundler can inline them, and throw if the result is undefined.
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;
if (!siteUrl) throw new Error("NEXT_PUBLIC_SITE_URL is not set for this build");
Keep one list of variables per environment and rebuild after changes
Record every variable with the environments it must exist in (Production, Preview, branch deploys, Development), and compare the list with the host's settings on each deploy. After adding or changing a NEXT_PUBLIC_ or VITE_ value, trigger a new build. The old bundle keeps the old value.
Rotate secrets in this order
Create the new credential, save it for the right environments, and redeploy. Confirm the new deployment works, and only then invalidate the old credential. If Preview deployments use the same secret, redeploy them too.
Register production redirect URLs and CORS origins explicitly
In your auth provider, set the Site URL and redirect allow list to the production domain (Supabase: Authentication, URL Configuration), and keep localhost as a separate entry. On your API, echo back only origins from an allow list and send Vary: Origin.
const allowed = new Set(["https://app.example.com", "http://localhost:3000"]);
const origin = request.headers.get("origin");
if (origin && allowed.has(origin)) {
headers.set("Access-Control-Allow-Origin", origin);
headers.set("Vary", "Origin");
}
Point uptime checks at the production domain
Check the Deployment Protection scope under Settings, Deployment Protection. Point uptime monitors at the production domain, not a generated *.vercel.app URL. If automation must reach a protected URL, use Protection Bypass for Automation instead of turning protection off.