← All security checksExposed secret keys

What is an exposed API key in a frontend bundle, and how do you find one?

An exposed API key in a frontend bundle is a secret credential — a Stripe secret key, a Supabase service-role key, an OpenAI key, a cloud provider key — that got compiled into the JavaScript a browser downloads, instead of staying server-side. Anyone can open your site's network tab, view the bundled .js files, and read the key in plain text. AI app builders make this common because generated code often calls third-party APIs directly from client components using an environment variable that was never meant to be public.

Why this happens in AI-generated apps

Tools like Lovable, Bolt, v0, Cursor, Replit, and Base44 generate working code fast, and "fast" often means the shortest path to a working demo: importing a service SDK straight into a client component and reading process.env.SOME_KEY there. In Next.js, any environment variable without the NEXT_PUBLIC_ prefix is meant to stay server-only — but a key with the wrong prefix, or a hardcoded string typed directly into a component during a prompt-driven edit, ships to every visitor's browser as plain text inside the compiled bundle.

The result looks identical to a working app in the demo. It only becomes visible as a problem when someone opens devtools, or when a scanner reads the same bundle a browser already downloaded.

How to find exposed keys yourself

Open your deployed site, open browser devtools, go to the Sources or Network tab, and search the loaded .js files for common key prefixes: sk_live_ and sk_test_ (Stripe), sb_secret_ or service_role (Supabase), sk- (OpenAI), and any string assigned to a variable named key, secret, token, or apiKey near a fetch or SDK initializer.

That manual check only catches what you think to search for. An automated scan reads every script your app actually serves and pattern-matches against a much larger set of known key formats — including ones you wouldn't recognize by sight.

How to fix an exposed key

Move the call that uses the key behind a server route (an API route, server action, or edge function) so the key never leaves your server. Then rotate the exposed key immediately — assume it's already been seen, since public bundles are routinely scraped by automated bots, not just curious humans.

  • Move the API call server-side (route handler, server action, or serverless function).
  • Rotate the exposed key in the provider's dashboard — treat it as compromised.
  • Restrict the new key's scope/permissions if the provider supports it.
  • Re-scan after deploying to confirm the key no longer appears in any shipped bundle.

Frequently asked

Is a NEXT_PUBLIC_ environment variable safe to use for API keys?

No — the NEXT_PUBLIC_ prefix in Next.js exists specifically to ship a value to the browser. It's correct for values that are meant to be public (like a publishable/anon key), never for a secret or service-role key.

Does deleting the key from my code remove it from users who already loaded the page?

It removes it from future page loads once redeployed, but the key was already sent to every browser that loaded the old bundle, and it may still sit in browser caches or scraped archives. Rotation, not deletion, is what actually closes the exposure.

//Get started//

Ready to make it unbreachable?

Point us at your app and get a full breakdown of what's exposed in about two minutes. Every finding comes with the fix.

Shipping with an AI builder? See how verification works