Environment Variables: Where Settings and Secrets Live
What environment variables are, why you need a .env file, why values differ between development and production, and why secrets never go in the frontend.
Environment variables are settings an app receives from outside when it starts, instead of keeping them in its code: the database address, API keys, the mode it runs in. The same code reads them and behaves differently depending on where it is running.
If you’ve seen a .env file in your project or lines like process.env.DATABASE_URL, that’s them. Here is why they exist, how they work and where vibe-coded projects most often get them wrong.
Why environment variables exist
Imagine the database address is written straight into the code. Then:
- switching from the production database to a test one means editing code;
- the database password ends up in Git and stays in the history forever, even if you delete it later;
- anyone with access to the repository has access to the data.
Environment variables separate two things: what the program does (code, identical everywhere) and what it works with (settings, specific to each place it runs). You can safely show the code to a contractor or make it public, while the settings stay with whoever runs it.
The .env file and how it works
On a server, variables are set in the hosting dashboard or the launch configuration. Locally it’s easier to keep them in a .env file at the project root, which your framework or a library reads on startup.
DATABASE_URL=postgres://localhost:5432/myapp
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_API_URL=http://localhost:3000
Three rules for this file:
.envalways goes in.gitignore. Check it now, not later.- Next to it sits
.env.examplewith the same names and no real values. That one is in Git, and it tells a new person what needs filling in. - If a
.envwith real keys ever reached the repository, treat those keys as leaked. Rotate them; deleting the file isn’t enough.
Why values differ in each environment
A normal project runs in at least two places: your computer and production. Often there’s also staging, a copy for checking things before release. The code is the same everywhere; the variables are not:
- locally, a test database and test payment keys;
- on staging, a separate database with fake data;
- in production, the real database and live keys that only the server knows.
That way a stray run on your laptop won’t charge a real card or email real customers. If your local .env holds live keys, you’re in exactly the situation environment variables are supposed to prevent.
The big trap: environment variables in the frontend
This is where people get confused most. Server-side environment variables stay on the server. Frontend variables, however, are written straight into the JavaScript the browser downloads at build time. After that, any visitor can read them in developer tools.
Frameworks mark these with a prefix: NEXT_PUBLIC_, VITE_, REACT_APP_, EXPO_PUBLIC_. The prefix doesn’t mean “handy variable”. It means “this string will be public”.
What’s fine to expose with that prefix:
- the URL of your API;
- a public analytics or maps key;
- the public Supabase or Firebase key, which is designed to be seen; your access rules protect the data, not the key.
What never belongs there:
- secret payment keys;
- keys for OpenAI, Anthropic and other paid APIs;
- service-level database keys that bypass access rules;
- passwords, email service tokens, signing keys.
If the frontend needs something that requires a secret, such as calling an AI model, the request goes through your server or a server function. The browser asks your server, and the server, holding the secret, calls the external API.
A couple more subtleties
- Variables are read at startup or at build time. Changed a value in the hosting dashboard? Restart the app, and often rebuild the frontend. Otherwise you’ll spend an hour wondering why nothing changed.
- An empty value is worse than an error. If a required variable is missing, the app should fail at startup with a clear message rather than run with a blank key.
- Don’t log variables wholesale. “Let me print the config for debugging” is a common way secrets end up in logs.
In short
- Environment variables are settings an app gets at startup rather than keeping in code.
.envstays out of Git;.env.examplewithout values goes in.- Development, staging and production have different values, and live keys exist only on the server.
- Anything prefixed
NEXT_PUBLIC_,VITE_and the like ships to the browser and becomes public. - After changing a variable, restart or rebuild the app.
If you’re not sure which keys have already made it into your bundle, SUBXCAT can check and sort it out.