Safeguard
Application Security•

How should you handle secrets in JavaScript apps and bundles?

Anything in a browser bundle is public. Learn how Vite and Next.js env prefixes work, what belongs on the server, and how to rotate a key that shipped.

Safeguard Security Team
4 min read

Treat every value that reaches a browser bundle as public. Secrets belong on a server, a serverless function or a CI secret store, and the browser should call that backend. Prefixed environment variables such as VITE_ and NEXT_PUBLIC_ are copied into shipped JavaScript on purpose, so never put keys there.

Why do prefixed variables leak?

Frameworks need a way to pass config to client code, so they expose a marked subset. Vite says variables prefixed with VITE_ are exposed in client-side source after bundling, and that such variables should not contain sensitive information such as API keys, because they are bundled at build time.

Next.js behaves the same way. A NEXT_PUBLIC_ variable is inlined into the JavaScript bundle at build time, replacing each reference with a hard-coded value. Without the prefix, a variable stays in the Node.js environment.

The trap is a developer who adds the prefix to make an error go away. The app works, and the key is now in every visitor's browser.

What can be public?

Some values are meant for the client: an analytics ID, a public map token restricted by domain, a publishable payment key. Check the provider's documentation. If the vendor says a key can be used client side, it should still be restricted by origin or scope. A secret key, a database password or a signing key is never in that group.

Where do secrets go instead?

Constructed example: a React dashboard calls a third party geocoding API with a paid key.

Wrong: VITE_GEOCODER_KEY in the .env file, read in the component.

Better: a small backend route holds GEOCODER_KEY in the server environment, the component calls your route, and the route calls the vendor. Add per-user rate limits so your route is not an open proxy for the paid service.

Vite's own guidance for production says to consider a backend server or serverless or edge functions to secure secrets.

What about .env files and CI?

Next.js notes that its default template adds .env files to .gitignore and that you almost never want to commit them. Vite's .local files are ignored by git, but ignore rules only work if they exist before the first commit. A secret committed once lives in history.

The OWASP Secrets Management Cheat Sheet adds the lifecycle: secrets should be rotatable, revocable, visible only to those who need them, and never logged. It also recommends detection rules and documented response steps.

What if a key already shipped?

OWASP's incident guidance is revoke, rotate, delete. Revoke the exposed key at once. Create a new one, ideally through an automated process. Remove the old value from code and logs. Rewriting git history can help but may cause other problems, and it does not undo exposure, because the bundle was already public.

Then check provider logs for use of the old key between exposure and revocation.

What to do

  1. Search your build output, not only your source, for keys. Source maps can reveal them too.
  2. List every variable with a public prefix and justify each one.
  3. Move secret values behind a server route or function.
  4. Restrict any key that must be public by origin, scope or quota.
  5. Add a pre-commit and CI secret scan.
  6. Store CI secrets in the platform store and keep them out of logs.
  7. Write a revoke and rotate runbook and practise it once.

Safeguard includes secrets scanning, which can flag a key in code before it ships.

What about server rendered frameworks?

Frameworks that render on the server blur the line, since one file may run in both places. Keep secret reads in server-only modules, and review any shared module that touches process environment values. A function that looks server side can still ship to the browser if a client component imports it, so check the built output rather than trusting file names.

Quick check

Is a .env file safe for secrets in a frontend project?

Only for server-side values. Anything with a public prefix is copied into the bundle at build time.

First step when a secret reached the browser?

Revoke it, then rotate and remove it from code and logs, per OWASP guidance.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.