Configuration and domains
Scope environment variables
Give Development, Preview, and Production their own values and understand that changes affect only deployments created afterward.
An environment variable on Vercel is not one value. It’s a name with a value per environment. When you add DATABASE_URL in the project settings, you tick which environments get it: Development, Preview, Production, or a custom environment on plans that support them. Preview values can even differ per branch.
This is how we keep a Preview deployment away from production data. Same code, same variable name, different database behind it.
Scope by what the value can do
Ask, for each variable, what happens if the wrong environment uses it. A wrong APP_ENV label is harmless. A wrong DATABASE_URL or STRIPE_SECRET_KEY is a real incident.
So Preview gets a test database, a test payment key, and an email provider in sandbox mode. Production gets the real ones. Branch-specific values are great for an integration test branch, but write down what an unconfigured branch falls back to. It should never silently inherit a dangerous target.
Changes apply to new deployments only
Here is the rule that trips people up. Deployments are immutable, and that includes their configuration. Changing a variable in the dashboard does nothing to the deployment that is live right now. You have to redeploy.
Let’s prove it. Add a variable named APP_ENV with the value preview for Preview and production for Production. Then add a small diagnostic page that renders it:
// app/diagnostics/page.tsx
export default function Diagnostics() {
return <p>Environment: {process.env.APP_ENV ?? 'not set'}</p>
}
Push it to a branch. The Preview shows Environment: preview. Merge it, and Production shows Environment: production. Now change the Preview value to preview-2 in the dashboard and reload the Preview URL. Still preview. Redeploy the branch, and it updates.
Old unique URLs keep running with the values they were built with. That has a security consequence: rotating a credential in Vercel isn’t enough. You also invalidate the old one at the provider, or the old deployment can still use it.
Don’t print secrets to prove they exist
The diagnostic page above shows a harmless label. For real secrets, render only a boolean:
<p>Database: {process.env.DATABASE_URL ? 'configured' : 'missing'}</p>
You learn what you need without leaking anything into HTML or logs. Remove temporary variables when the test is over.
Try this on your own project: add APP_ENV with different Preview and Production values, redeploy both environments, and check the diagnostic page on each URL. Then change one value without redeploying and watch nothing happen.
Lesson completed