VibeCoden't

Guide · September 6, 2026

Exposed API keys in frontend code: how to find and fix them

Everything your app sends to the browser is public. Not "obscure" — public. Anyone can open devtools, read your JavaScript bundle, and copy any string in it. The question is never "can someone see this key?" (they can) but "does seeing this key give them power?"

Publishable keys vs secret keys

Most providers ship two kinds of credential, and the naming is the whole game:

  • Publishable / anon / client keys — designed to be seen. They identify your project, and the server still enforces per-user permissions behind them. Examples: Stripe pk_live_…, Supabase anon/publishable keys, Firebase web config, Google Maps browser keys.
  • Secret / service / admin keys — designed to be hidden. They bypass permission checks and act as the project owner. Examples: Stripe sk_live_…, Supabase service-role keys, OpenAI sk-…, AWS access key pairs, SendGrid/Resend API keys, database connection strings.

A secret key in a frontend bundle is not a warning — it is an active incident. Anyone who finds it can read and write your data, send email as you, or spend your credits.

Why AI-generated apps leak keys so often

When you ask an assistant to "make this work", the shortest path to working code is usually to call the API directly from the component. That produces the same handful of mistakes over and over:

  • A server-only key given a client-visible prefix (VITE_, NEXT_PUBLIC_, REACT_APP_) so the build stops complaining. That prefix is exactly what inlines it into the bundle.
  • A key pasted straight into a component as a fallback "for now".
  • An admin client imported into a shared helper, which then gets imported by a page — dragging the secret into the client graph.
  • .env committed to a public repository, or left readable at /.env on the deployed site.
  • Source maps published in production, exposing your original files verbatim.

How to check your own site in five minutes

  1. Open your live site, then devtools → Sources. Search all files (Ctrl/Cmd+Shift+F) for sk_, service_role, secret, PRIVATE, BEGIN RSA, and password.
  2. Try https://yoursite.com/.env and /.git/config in a browser. Both should 404.
  3. Check the Network tab: does any request carry an Authorization header your users shouldn't be able to forge?
  4. Look for .map files next to your JS bundles. In production, don't ship them.
  5. Search your git history, not just the working tree — git log -p -S "sk_live" finds keys you already "deleted".

If you already leaked a key

Order matters. Do this:

  1. Rotate first. Deleting the line from your code does nothing — the key is still valid and already scraped. Issue a new key in the provider dashboard and revoke the old one.
  2. Check the logs. Most providers show recent API usage by key. Look for calls you didn't make, from IPs you don't recognise.
  3. Move the call server-side. The browser should call your backend; your backend holds the key and decides what the user is allowed to do.
  4. Scope what remains. Restrict keys by domain, IP, or permission wherever the provider supports it, and set a spend limit.
  5. Purge history if it was committed. Rewrite the repo history or, faster and safer, rotate and move on — assume the old value is permanently public.

The rule that prevents all of this

If a credential grants an ability you would not grant to an anonymous stranger, it must never be reachable from browser code — no environment variable prefix, no bundler trick, no obfuscation. Keep it on the server, behind a function that first checks who is asking.

Find leaked keys automatically

VibeCoden't scans your live site and your uploaded code for secrets, exposed .env files, published source maps, and admin clients that shipped to the browser — then gives you a fix prompt for each one.

Run a free scan

Related: what a vulnerability scan actually checks.