[GUIDE]

How to Find Exposed API Keys on Your Website (and What to Do)

If you suspect an API key is exposed on your website, you can confirm it in a couple of minutes, and if it is real, the single most important action is to rotate the key immediately at the provider. Open your browser's DevTools, search the loaded JavaScript for the key's value or its prefix (sk-, AKIA, sk_live_), and try fetching /.env and /.git/config directly. If a secret shows up, revoke and reissue it, then move the call server-side so the key never reaches the browser again.

This guide walks through why keys leak into frontend code, how to check for them, and what to do the moment you confirm one is exposed.

How do API keys end up exposed on a website?

Client-side code is fully readable by anyone who visits your site. Your JavaScript runs in the visitor's browser, which means the browser must download it — and anything in that download can be inspected. A few common ways secrets end up there:

  • Bundled into JavaScript. You reference a secret key in frontend code (or in a framework env var that the bundler inlines), and your build tool bakes the literal string into the shipped bundle. Minification does not hide it — the value survives as a plain string. This is the textbook case of CWE-798: Use of Hard-coded Credentials.
  • A committed .env file served at the web root. If .env, .env.local, or .env.production ends up inside your deployed public/ or web root, anyone can fetch it directly. These files routinely contain database URLs, JWT secrets, and API keys.
  • Source maps in production. Source maps reconstruct your original, un-minified source — including any inlined secret. See web.dev's overview of source maps for what they expose.
  • Public config endpoints. A /config.json or a window.__CONFIG__ blob in your HTML that someone forgot should be server-only.
  • A served .git/ directory. If .git/config or .git/HEAD is reachable, an attacker can often reconstruct your whole repository — and every secret ever committed to it.

This is especially common in AI-assisted and "vibe coded" projects, where a generated snippet calls an API directly from the frontend with the secret key inline. We cover that pattern in depth in the security risks of vibe coding.

How to check if your API key is exposed

You can run all of these checks yourself in a few minutes.

1. Search your loaded JavaScript. Open DevTools (F12) → the Sources or Network tab → look at the .js files your page loads. Use the global search (Ctrl/Cmd-Shift-F in Chrome DevTools) and search for the key's exact value, or its prefix:

ProviderSecret-key prefix to search for
OpenAIsk-
Stripe (secret)sk_live_ / sk_test_
AWSAKIA
Google / GCPAIza (then check restrictions)
Private keys-----BEGIN

2. Try the file paths directly. In your browser, visit https://yourdomain.com/.env, then /.env.local, /.env.production, and /.git/config. A 404 is good. KEY=VALUE lines or [core] config text means the file is public.

3. Check your git history. SteelSuit scans your deployed site from the outside and does not read your repository — but a secret can live in git history even after you delete it from the current code. Run trufflehog or gitleaks locally against your repo to catch those. If you find one, the key is compromised and must be rotated regardless of whether the file is still present.

4. Run an automated external scan. Doing all of the above by hand for every page and every key is tedious. SteelSuit fetches your site's HTML and JavaScript bundles and runs TruffleHog's 800+ detectors over them (pattern-match only — it uses --no-verification, so it never makes a call with a discovered key). It also probes for exposed sensitive paths including .env, .git/, robots.txt, and sitemap.xml. Each exposed-secret match is reported with a severity: cloud, financial, database, and private-key detector classes are flagged high. It is a black-box scan of your live domain — no agent, no source-code access. This makes it a fast way to see exactly what a stranger with a browser could read off your site, which complements the broader web application security checklist.

My API key is exposed — what do I do right now?

Work through these in order. Do not skip step 1.

  1. Rotate the key immediately. Revoke the exposed key and issue a new one at the provider. This is non-negotiable and time-sensitive: a leaked key is live until you kill it. (For example, OpenAI's best practices and Stripe's API key docs both walk through revoking and rolling keys.)
  2. Move the call server-side. Put the secret behind a backend route or serverless function. The browser calls your endpoint; your server holds the key and calls the provider. The key never ships to a client again.
  3. Restrict the new key's scope. Apply least privilege — limit it to the specific permissions, IPs, or referrer domains it actually needs, and set spend/rate limits where the provider supports them.
  4. Remove the leaked file. If a .env or config file was served, delete it from your web root and add it to .gitignore. Use your host's secret manager or platform environment variables instead.
  5. Scrub git history if needed. If the secret was ever committed, removing it from the latest code is not enough — rewrite history with git filter-repo or BFG so the value is gone from every commit.
  6. Check for abuse. Review the provider's usage and access logs for unexpected activity between the leak and the rotation.

For the durable patterns behind step 2 — keeping secrets server-side and shipping clean frontends — OWASP's Secrets Management Cheat Sheet is the canonical reference.

Which API keys are safe to expose, and which are not?

Not every key in your frontend is a problem. Many providers deliberately split keys into a public class and a secret class. The public ones are designed to be visible because the security boundary lives elsewhere (server-side rules, domain restrictions). The secret ones grant privileged access and must never leave your server.

Key typeSafe in frontend?What to do
Stripe publishable key (pk_)Yes — designed to be publicLeave it; it can only create tokens, not move money
Supabase / Firebase anon keyYes — public by designSafe only if row-level access rules are configured; the key alone grants nothing extra
Google Maps / Places keyYes, if restrictedLock it to your domain (HTTP referrer) and specific APIs; an unrestricted key is abusable
Stripe secret key (sk_live_)NoRotate now; move all charges server-side
OpenAI / LLM secret key (sk-)NoRotate now; proxy through your backend
AWS access key (AKIA...)NoRotate now; never embed cloud credentials client-side
Supabase service-role keyNoRotate now; it bypasses access rules entirely
Private keys (-----BEGIN ...)NoRotate now; remove from the bundle and any served files

The rule of thumb: if the provider calls it publishable, anon, or client, it is meant to be seen. If it says secret, service, or server, it is not. When you are unsure, treat it as secret and keep it on the server — the cost of being wrong in that direction is just an extra backend call; the cost of the other mistake is a compromised account.

Supabase is the textbook case here, since its anon key is meant to be public while its service-role key bypasses every access rule — Supabase security: what an external scan can and cannot tell you covers that distinction and why Row Level Security, not the key, is the real boundary.

The takeaway

Exposed secrets are one of the most common and most damaging issues on modern sites, and they are also one of the easiest to check for. Search your bundles, probe /.env and /.git/config, scan your git history, and — if you want it done automatically across every page and bundle — run an external scan. If you do find a real secret key in client-side code, rotate it first and ask questions later. A rotated key costs you a few minutes; a live leaked key can cost you your bill, your data, or your account.

Frequently asked

Is it safe to put an API key in JavaScript?

It depends on the key type. Publishable or anon keys (Stripe pk_, Supabase anon, a Google Maps key locked to your domain) are designed to be public and are fine in client-side JavaScript. Secret or service keys (OpenAI sk-, Stripe sk_live_, AWS access keys, a Supabase service-role key) are never safe in JavaScript — anyone who opens DevTools can read your entire bundle, so the key is effectively published to every visitor.

What happens if my OpenAI API key is exposed?

Anyone who finds the key can make API calls billed to your account, potentially running up large charges and exhausting your quota before you notice. If your OpenAI API key is exposed, revoke it immediately in the API keys section of your OpenAI dashboard, create a new one, and move all OpenAI calls to a backend server so the key is never sent to the browser.

How do I check if my .env file is public?

Open your browser and navigate directly to https://yourdomain.com/.env (also try /.env.local and /.env.production). If you see KEY=VALUE lines instead of a 404, the file is publicly served and must be removed from your web root immediately, with every secret it contained rotated. An external scanner can probe these paths for you automatically.

How do I know which API keys are safe to expose?

Check the provider's documentation. Most modern providers split keys into a public class (publishable, anon, client) and a secret class (secret, service-role, server). Public keys assume the client is untrusted and rely on server-side rules to stay safe; secret keys grant privileged access and must stay on the server. When in doubt, treat the key as secret and keep it server-side.

Can someone steal my API key from a minified or bundled file?

Yes. Minification and bundling only change formatting and variable names — the literal string value of an API key survives intact and is trivially found with a text search. Source maps make it even easier by reconstructing your original source. Bundling is not a security control; never rely on it to hide a secret.