How to Find Exposed API Keys in Your Frontend

Secret keys in your client bundle are public to anyone with devtools. Here is how to find leaked API keys in your frontend and what to rotate first.

By Artem Vysotskyi · Updated

Anything shipped to the browser, NEXT_PUBLIC_ variables, hardcoded keys, source maps, a committed .env file, is readable by anyone who opens devtools or views your page source. There is no client-side hiding place. The fix is to find what is already exposed, move real secrets server-side, and rotate the keys that leaked.

Why frontend keys are public by design

Anything that runs in the browser is downloadable. Your JS bundle, your .env-derived build output, your inline script tags, all of it is just text a visitor's browser fetches and executes. There is no sandbox that keeps a string secret once it ships to the client, no matter how it is obfuscated or minified.

This is not a bug you can code around. It is why the entire model of "public" versus "secret" keys exists: some keys are designed to be seen (an anon key, a publishable key), and some are designed to never leave your server. The mistake is treating a secret key as if shipping it to the frontend were a configuration detail instead of a leak.

Safe vs unsafe keys

Most platforms give you two classes of key, and mixing them up is the single most common leak LeakRank sees.

Safe to expose (public, publishable, anon):

  • Stripe's pk_ publishable key
  • Supabase's anon key
  • Firebase's apiKey (an identifier, not a secret)
  • Any key explicitly documented as "safe for client-side use"

Never expose (secret, private, service-role):

  • Stripe's sk_ secret key
  • Supabase's service_role key
  • Any third-party API key without a documented public/private split (assume it is private by default)

The prefix or naming convention exists precisely so you can tell them apart at a glance. When a platform does not clearly label a key, treat it as secret until proven otherwise.

How to find leaked keys

Browser devtools. Open your live site, open devtools, and check the Sources and Network tabs. Search the loaded JS for key, secret, token, and any provider prefixes you use (sk_, service_role, etc.). If it is visible here, it is visible to everyone.

Search the JS bundle directly. Fetch your built bundle and grep it:

curl -s https://yourapp.com/_next/static/chunks/*.js | grep -oE '(sk_live_|service_role)[A-Za-z0-9._-]+'

This catches keys that are technically minified but not actually hidden, minification changes variable names, not string contents.

Check source maps in production. If source maps are deployed alongside your build, anyone can reconstruct readable source from the minified bundle, including any secret that was inlined at build time rather than fetched at request time. Confirm .map files are not publicly reachable in production.

Check .env in the repo. Run git log --all -- .env and grep your full history, not just the current tree, for key-shaped strings. A .env committed once and later deleted is still in git history and still exposed to anyone with repo access, including a public GitHub repo indexed by scrapers.

Move secrets server-side

The structural fix is to never let a secret key exist in code that ships to the browser. Concretely:

  • Call third-party APIs that require a secret key from a server route or Edge Function, not from client-side code.
  • In Next.js, only variables prefixed NEXT_PUBLIC_ are exposed to the browser, everything else stays server-only, but only if you actually read it in server code. A secret key stored without the prefix but still referenced inside a client component gets bundled anyway.
  • Proxy the request: client calls your server, your server calls the third party with the secret attached, the response comes back without the key ever touching the browser.

Rotate the keys you leaked

Finding a leaked key is not the fix, rotating it is. Once a secret key has been in a public bundle, a public repo, or a source map, assume it has been copied. You have no way to know who has it or how long they have had it.

For each leaked key: generate a new one in the provider's dashboard, update your server environment variables, redeploy, then revoke the old key. Check the provider's usage or audit logs for the affected window for activity you cannot explain.

Prevent it next time

  • Add a pre-commit check that greps staged files for known secret prefixes before they can be committed.
  • Keep a documented list of which keys on your team are public-safe and which are not, so new engineers do not have to guess.
  • Re-run a check like this after every dependency or SDK upgrade, some SDKs default to inlining config in ways that change between versions.

Scan your live URL for exposed keys, free

FAQ

Are NEXT_PUBLIC_ env vars safe to use?

Only for values that are meant to be public, like a publishable key or a project ID. `NEXT_PUBLIC_` does not add protection, it is the opposite: it explicitly tells the build system to bundle that variable into the client. Never put a secret key behind that prefix. #

Can I hide an API key in the frontend?

No. Obfuscation, minification, and even simple encoding do not hide a string from someone who opens devtools or downloads your bundle. If a key must stay secret, it cannot exist in any code that reaches the browser, it has to live server-side. #

How do I know if a leaked key was abused?

Check the provider's dashboard for usage or audit logs during the window the key was exposed. Most platforms (Stripe, Supabase, most cloud providers) show request volume, source IPs, or billing spikes that indicate whether the key saw traffic you did not generate.: link LeakRank's own scan history if it surfaces this. #

Should I rotate a key that was only briefly exposed?

Yes. There is no way to confirm a key was not copied in the window it was visible, scrapers and bots index public repos and deployed bundles continuously, often within minutes. Treat any exposure window, however short, as a full leak and rotate.

Find out in 30 seconds

Paste your URL and LeakRank shows what a stranger can actually read from your app. Free, no signup.

Scan my app free →

Related guides