Vibe coding security

Which environment variables get exposed to the browser

Most frameworks decide what's public by naming convention: a specific prefix tells the build tool to embed a value into the client bundle, where anyone can read it. Here's the reference list — and what must never use one.

Short answer

Frameworks expose environment variables to the browser by prefix: a variable named with a recognized public prefix is bundled into your client JavaScript, where every visitor can read it. The common ones are NEXT_PUBLIC_, VITE_, REACT_APP_, PUBLIC_, EXPO_PUBLIC_, and GATSBY_. These are safe only for public identifiers — never put a secret key behind one.

Key takeaways

  • Many frameworks expose environment variables to the browser by naming convention: a specific prefix tells the build tool to embed that value into the public client bundle.
  • An exposed env variable is not encrypted or hidden — anyone can read it in the page's JavaScript. It's only safe for public identifiers.
  • Common client-exposed prefixes include NEXT_PUBLIC_ (Next.js), VITE_ (Vite), REACT_APP_ (Create React App), PUBLIC_ (Astro/SvelteKit), EXPO_PUBLIC_ (Expo), and GATSBY_ (Gatsby).
  • Never put a secret — a database service key, payment secret, or private API key — behind one of these prefixes. It will ship to every visitor.
  • When in doubt, assume any value the frontend can read is public, and keep real secrets in server-side code or environment variables without a public prefix.

The client-exposed prefix reference

Each entry below is the prefix that a framework's build tooling treats as "ship this to the browser." Any variable using it becomes part of the public bundle.

  • NEXT_PUBLIC_

    Next.js. Variables prefixed this way are inlined into the browser build; everything else stays server-only. Used by v0 and many AI-built Next.js apps.

  • VITE_

    Vite. Exposed via import.meta.env in the client. This powers Lovable and many Bolt.new apps, so VITE_ is one of the most common places secrets leak.

  • REACT_APP_

    Create React App. Any REACT_APP_ variable is embedded into the static build and visible in the browser.

  • PUBLIC_

    Astro and SvelteKit. PUBLIC_-prefixed variables are exposed to client code; unprefixed ones are kept private to the server.

  • EXPO_PUBLIC_

    Expo (React Native, including web). EXPO_PUBLIC_ variables are embedded into the app bundle that ships to devices and browsers.

  • GATSBY_

    Gatsby. GATSBY_-prefixed variables are made available to browser code at build time.

  • NUXT_PUBLIC_ / public runtimeConfig

    Nuxt. Values placed under the public runtime config (often via NUXT_PUBLIC_) are sent to the client; private config stays server-side.

  • PLASMO_PUBLIC_ / WXT_PUBLIC_

    Browser-extension frameworks (Plasmo, WXT). Public-prefixed values are bundled into the extension's client code.

The list isn't exhaustive — the rule is

Tools change and new frameworks add their own conventions. The durable rule: any value the frontend can read is public. If you're unsure whether a variable ships to the browser, assume it does and keep real secrets out of it.

Safe to expose vs. never expose

A public prefix is correct for values that are meant to be seen, and dangerous for anything that grants privileges.

Safe behind a public prefix

  • A public API base URL or your app's own domain.

  • A Supabase project URL and the Supabase anon (public) key — designed to ship in the client.

  • A provider's publishable key (e.g. a Stripe publishable key) explicitly meant for the browser.

  • Non-sensitive feature flags and analytics site IDs.

Never behind a public prefix

  • A Supabase service_role key or any database admin/service key.

  • A Stripe secret key or other payment secret.

  • A private API key — OpenAI, email/SMS providers, or any third-party secret.

  • Webhook signing secrets, JWT signing keys, or any private token.

Secrets belong in server-side code — API routes, server actions, edge or serverless functions — using variables without a public prefix. For the broader picture, see public .env files & exposed secrets.

What to do if you exposed a secret

If a secret shipped behind a public prefix, treat it as compromised — it may already be in someone's cache or logs.

  • Rotate the key immediately in the provider's dashboard so the exposed value stops working.

  • Move the secret to a server-side variable without a public prefix, and reference it only from server code.

  • Redeploy and confirm the new bundle no longer contains the secret.

  • Run a scan of the live URL to confirm nothing else is exposed.

The full recovery steps are in public .env files and what to do if a secret leaked.

Frequently asked questions

Which environment variable prefixes are exposed to the browser?
The most common client-exposed prefixes are NEXT_PUBLIC_ (Next.js), VITE_ (Vite, including Lovable and many Bolt apps), REACT_APP_ (Create React App), PUBLIC_ (Astro and SvelteKit), EXPO_PUBLIC_ (Expo / React Native web), GATSBY_ (Gatsby), and NUXT_PUBLIC_ / runtime public config (Nuxt). Any variable with one of these prefixes is embedded into the public JavaScript bundle and is readable by every visitor.
Is it safe to put an API key in a NEXT_PUBLIC_ or VITE_ variable?
Only if the key is meant to be public. A Supabase anon key or a provider's publishable key is designed to be seen and is fine in a public-prefixed variable. A secret key — a database service_role key, a Stripe secret key, a private AI API key, or any token that grants real privileges — must never use a public prefix, because it will be bundled into the browser where anyone can read it.
Why do frameworks expose some environment variables on purpose?
Frontend code runs in the browser, so it sometimes needs configuration values — a public API URL, a publishable key, a feature flag. Frameworks use a naming convention (the prefix) so you explicitly opt a variable into the client bundle, and everything without that prefix stays server-only by default. The prefix is a safety boundary: it forces you to consciously mark a value as public.
How do I check if I exposed a secret in my frontend?
Search your code for any secret referenced through a public-prefixed variable, and open your deployed site's JavaScript bundle (via browser dev tools) to look for keys. A scan of your live URL can also flag secrets that shipped to the frontend. If a secret has already been exposed, rotate it immediately — the old value should be treated as compromised.

Check what your frontend is exposing

GuardMint scans your live app's bundle for keys and secrets that shipped to the browser — and flags the serious ones. No signup required for your first score.

Which Environment Variables Get Exposed to the Browser | GuardMint