What the API key leak checker looks for
It reads the text you paste and matches it against the formats providers give their keys: OpenAI, Anthropic, Gemini, Groq, Stripe, Clerk, AWS, Supabase, Firebase, GitHub, Slack, Twilio, SendGrid, Resend, Hugging Face and more, plus database URLs with a password, private keys and signing secrets such as JWT_SECRET. For each match it tells you three things: whether the key is meant to be public, where it sits (a browser variable, a bundle, a commit, a server-only env variable), and what to do about it.
| Rating | Examples | What it means |
|---|---|---|
| Secret | sk-proj- (OpenAI), sk-ant- (Anthropic), sk_live_ (Stripe or Clerk), Supabase service_role and sb_secret_, AWS AKIA with its secret key, ghp_ (GitHub), postgres://user:password@ | Anyone who reads it can use it, on your bill or with your data. Rotate it. |
| Public by design | Supabase anon and sb_publishable_, the Firebase apiKey, Stripe pk_live_, PostHog phc_, a Sentry DSN, Mapbox pk. | Meant for browsers. What protects you is behind it: RLS, Security Rules, key restrictions. |
| To check | Google AIza keys outside a Firebase config, sk- keys of other AI providers, random values under names like SECRET or TOKEN | Could be either. The row says how to find out. |
Why each check matters
- The variable prefix.
NEXT_PUBLIC_,VITE_,REACT_APP_andEXPO_PUBLIC_tell the build to copy the value into the JavaScript every visitor downloads. The Next.js docs say the value "will be inlined into any JavaScript sent to the browser", and the Vite docs sayVITE_*variables "should not contain sensitive information such as API keys". A secret under one of these names is flagged even when the rest of the file is fine. - The Supabase role. The anon key and the service role key look alike: both start with
eyJ. The checker decodes the token in your browser and reads its role. The anon key is public; a service role key ignores row level security and opens every table. - The context of a Google key. Firebase says its keys "only identify your Firebase project" and may sit in code. But in February 2026 Truffle Security showed that Google keys published for Maps or Firebase can call Gemini once the Generative Language API is on in the same project. The checker reads the code around the key (a Firebase config, a Maps loader, a Gemini client) and rates it from that.
- Stripe or Clerk. Both use
sk_live_andsk_test_. The names around the key usually tell them apart; when they do not, the result says "Stripe or Clerk" instead of guessing. - The browser flag. The OpenAI and Anthropic SDKs refuse to run in a browser unless
dangerouslyAllowBrowseris set, because the key they hold would ship to every visitor. Finding it in your code is a strong sign a key is in your bundle. - Commits. In a git diff, keys on added lines are marked as committed. Deleting the line later does not remove the key from history.
How to read the result
The verdict comes first, in one sentence. Red means at least one secret sits where a stranger can read it: a browser variable, a bundle, a commit, or hard-coded in code. Amber means secrets only in server-only variables of an env file, which is where they belong as long as the file stays private, or values worth a second look. Green means only public keys, or nothing found.
Each row shows the value redacted to its first 6 and last 4 characters, its line, and the variable it is stored under. Then what a stranger can do with it, the steps in order with a link to the provider's dashboard, and code you can copy. The "Copy a fix prompt" button gives your coding agent the list of findings, still redacted, and the changes to make.
How to fix a key the checker flags
- 1
Create a new key
In the provider's dashboard, create a replacement and put it in your host's server environment, under a name without a public prefix.
- 2
Revoke the old one
Until you do, anyone who copied it keeps using it. Rotating without revoking leaves the leak open.
- 3
Read the usage and billing logs
Look for calls, charges or data reads since the key was first deployed or committed.
- 4
Move the call to your server
The browser calls your route; your route checks the user and calls the provider with the key.
// app/api/ai/route.ts: runs on the server, the key never reaches the browser
export async function POST(req: Request) {
// Check the caller is signed in first, or anyone can spend your credits.
const { prompt } = await req.json();
const res = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ model: "YOUR_MODEL", messages: [{ role: "user", content: prompt }] }),
});
return new Response(res.body, { status: res.status });
}# .env.local
# Before: NEXT_PUBLIC_OPENAI_API_KEY=sk-proj-...
OPENAI_API_KEY=your-new-key
# .gitignore
.env*
!.env.exampleA route that holds the key still needs a sign-in check and a rate limit, or strangers call your route instead of the provider: see how to protect an AI endpoint. The full incident playbook, provider by provider, is in what to do when an API key leaks.
What a static check cannot tell you
- Whether a key still works. Nothing is sent to any provider, so a revoked key and a live one look the same. Treat every secret it finds as live until you have revoked it.
- Keys without a fixed format. Mistral or Algolia admin keys are plain random strings. They are caught by the name they are stored under, with lower confidence, or not at all.
- The rest of your app. A paste is one file. Your live app loads many scripts, and some keys arrive at run time from an API response. The free check loads the app like a visitor and reads all of them.
- What a public key opens. The anon key is only harmless if row level security holds. In CVE-2025-48757, 170 of 1,645 Lovable apps tested had missing or weak RLS, about one in ten. Use the Supabase RLS checker or the Firebase rules checker for the rules behind your public keys.
Questions
Is it safe to paste my API keys into this checker?
The scan runs in your browser tab: no request carries what you paste, and analytics only count the verdict, never the text. You can check it yourself by turning off your network before you paste. If a key was ever pasted somewhere you do not trust, rotate it anyway.
How do I find API keys exposed in my frontend JavaScript?
Open your live app, press F12, then Ctrl+Shift+F (Cmd+Option+F on a Mac) to search every loaded script for sk-, sk_live, service_role or AIza. Or copy the main bundle from the Sources tab and paste it here. The free check does the same from your app's address.
Is it OK to have an API key in frontend code?
Only keys built to be public: the Supabase anon or publishable key, the Firebase config key, Stripe pk_ keys, PostHog phc_ keys and restricted Maps keys. Secret keys never belong there, and a NEXT_PUBLIC_ or VITE_ prefix publishes whatever it holds. More in API key exposed in frontend.
What should I do if my API key was leaked?
Create a new key, revoke the old one, then read the provider's usage and billing logs for activity you did not cause. Deleting the commit or the line does not help: anyone who saw the key still has it. Then move the call to a server route so the new key never reaches the browser.
Does this leaked API key checker test whether a key still works?
No. It sends nothing to any provider, so it cannot tell a revoked key from a live one, and it rates every secret as if it works. A full Keybrum attack on your live app looks for leaked secrets in what the app serves and proves each finding with the request that worked.
Why does the checker say my Firebase or Google Maps key is public?
Google designed those keys to sit in web pages: they identify your project, and your Security Rules or key restrictions decide what they can do. Restrict each key to the APIs it needs and to your domains, and keep the Generative Language API off any key that is in a browser.