Vibe Coding

Environment variables done right (and where they leak)

Right idea, easy to undo

Environment variables exist so you do not hardcode secrets into your source. You put DATABASE_URL or STRIPE_SECRET_KEY into the environment, read it at runtime, and the value never lives in a committed file. That part is solid and you should absolutely do it.

The trouble is the second half: in modern frontend tooling, some environment variables are deliberately shipped to the browser. If a secret lands in that group, the env var did not protect it at all — it just gave you a false sense that it did.

The prefix that changes everything

Frontend frameworks need a way to pass public config into client code — an analytics ID, a publishable key, an API base URL. They do this with a naming convention: a variable becomes browser-visible if it starts with a specific prefix. Common ones:

  • NEXT_PUBLIC_ in Next.js
  • VITE_ in Vite-based apps
  • PUBLIC_ in some setups
  • REACT_APP_ in older Create React App projects

At build time, every variable with that prefix is baked into the JavaScript bundle as a plain string. That is by design — but it means the prefix is a publish switch. Put NEXT_PUBLIC_STRIPE_SECRET_KEY in your env and you have shipped your secret key to every visitor, prefix and all.

The simple rule

  • Public prefix = public value, always. Only put non-sensitive things behind a public prefix: analytics IDs, publishable keys, public URLs. Never a secret.
  • Secrets stay unprefixed and server-side. A real secret should only ever be read in server code — an API route, a backend function — never in a component that ships to the browser.
  • When in doubt, assume the browser sees it. If you cannot say for certain a variable stays server-side, treat it as public and do not put a secret there.

Where env vars still leak

Even with the prefix understood, a few classic gaps remain:

  • The .env file gets committed. It belongs in .gitignore. Committed once, it lives in git history even after deletion.
  • .env deployed into a public folder. If the file ends up served as a static asset, it is one predictable URL away — /.env is a path bots try constantly.
  • Server var read in client code. Referencing an unprefixed secret from a component can, depending on the bundler, either fail or quietly inline it. Keep the boundary clean.
  • Secrets printed into logs during a build or error, where they end up in CI output.

A quick mental check

Before you ship, ask one question per variable: would I be comfortable pasting this value into a public tweet? If yes, a public prefix is fine. If not, it stays server-side, unprefixed, and out of any file the browser or a repo can reach.

Check what actually shipped

The hard part is that a secret baked into a bundle is invisible once it is minified. An automated scan reads your shipped pages and scripts the way a bot would and flags secrets and keys that are reachable from the browser. Scan your site and confirm your env vars stayed where you put them.

Related reading

FAQ

Are environment variables safe for secrets?
Server-side, yes — that is exactly what they are for. The risk is the public prefix: any variable starting with something like NEXT_PUBLIC_ or VITE_ is baked into the browser bundle, so a secret placed there is shipped to every visitor.
What goes behind a public prefix and what does not?
Only non-sensitive values: analytics IDs, publishable keys, public API URLs. Real secrets — database URLs, secret API keys, tokens — stay unprefixed and are read only in server code, never in client components.
Is committing a .env file a problem?
Yes. Add .env to .gitignore before the first commit. Once committed it stays in git history even if you delete it later, and if the file is deployed into a public folder it can be fetched directly at a predictable path like /.env.