Leaked API Key: How to Find and Revoke It


Contents
- What a leaked API key actually costs you
- How to find a leaked API key
- Revoke the key first, then clean up
- How to remove the key from git history
- Rotate without breaking your app
- The fix prompt for your coding agent
- How to stop leaking keys in the first place
- Frequently asked questions
- What happens if an API key is leaked?
- Why is exposing API keys bad?
- Can I just delete the file that contains the key?
- How do I know if my API key has been leaked?
- Does revoking a key break my app?
- Sources
A leaked API key is a bearer credential left in the open: anyone who finds the string can spend your credits, read your data, or impersonate your app, with no login required. Fix it in this order — find where the key leaked, revoke it in the provider's dashboard, then scrub it from git history so it never comes back. Here is exactly how to do each step.
What a leaked API key actually costs you
An API key is a bearer credential. Possession is proof: whoever holds the string can act as you. There is no password prompt and no second factor, so if the string is public, anyone who finds it is now you — for whatever that key can do.
The damage depends on which key leaked:
- A Stripe secret key lets someone create charges, issue refunds, and read your customers' data.
- An OpenAI or Anthropic key gets run up on your credit card. Attackers resell these keys in bulk and drain accounts in hours.
- A Supabase service_role key bypasses every permission rule and gives full read/write to your database. It is the worst possible secret to expose because it cannot be scoped down — here is where a service role key must never appear.
- An AWS access key can spin up instances or empty a bucket, and the bill still lands on you.
Bots scan public repositories for these strings continuously. A secret pushed to a public repo is usually found and used within minutes, not days, so "I'll fix it this weekend" is not a plan. The recovery sequence is short, and every step is reversible except the bill. Start now.
In AI-built apps the leak almost always happens the same way: you paste a key to your coding agent, the agent writes it into a source file or a .env that never made it into .gitignore, and the next commit publishes it. The agent does not flag this on its own.
How to find a leaked API key
You usually learn about a leaked API key one of three ways: an alert, a bill, or a scan. Work through these in order.

Photo by Ilya Pavlov on Unsplash
1. Check GitHub secret scanning. If your repo is public, GitHub scans every push for known secret patterns — AWS, Stripe, OpenAI, Google, and dozens more. When it finds one, it alerts repository admins and, where a partner integration exists, notifies the provider directly (some providers revoke the key for you). Look under your repo's Settings → Code security → Secret scanning for a list of detected secrets.
2. Run a secret scanner yourself. Two open-source tools do this well. gitleaks scans your git history, working tree, and commit diffs:
gitleaks detect --source .
trufflehog reaches further — it can scan a whole GitHub organization and finds secrets that are not in a standard key format:
trufflehog git file://. --only-verified
3. Search your own history directly. A key can live in a commit you never see in the working tree. git log -S searches every commit's changes for a string:
git log -S 'sk_live_1234' --source --all --oneline
Replace sk_live_1234 with the start of your key. If it returns commits, the key is in history.
4. Check whether the key is already public. Search a distinctive substring of your key on GitHub code search. If results appear, someone else can already see it — assume it is compromised and move to revocation, not cleanup.
5. Scan the live app, not just the repo. The most expensive leaks are the ones you shipped: a key baked into the JavaScript bundle, an API route that returns too much, or a source map that reconstructs your .env. Rustine's quick scan looks at your site the way a stranger would and shows what leaks, with the request and the response as proof. If your app was built with an AI coding agent, the MCP server can run that scan itself and apply the fix.
Revoke the key first, then clean up
This order is non-negotiable: revoke before you rewrite history.
Rewriting git history takes minutes and does not stop anyone who already copied the key. Revocation is instant, and it is the only step that actually cuts off the attacker. Do it in the provider's dashboard, not in your code:
- Log in to the provider (Stripe, OpenAI, Supabase, AWS, Google Cloud, GitHub, and so on).
- Find the leaked key and choose Revoke or Delete.
- Note its scopes before you delete it — you need them for the replacement.
- Generate a new key with the narrowest permissions that still work.
A leaked GitHub personal access token is a special case: revoke it under Settings → Developer settings → Personal access tokens, because that token can push code as you.
Do not skip revocation because the key is "only in an old commit." History is public on a public repo, and someone already copied it.
How to remove the key from git history
Deleting the file and pushing does not remove the key — every past commit still contains it. On a public repo you must rewrite history, then force-push. Even then, treat the key as compromised and revoke it first (see above). Rewriting history is about cleanup, not security.
The current tool is git filter-repo, faster and safer than the older git filter-branch. Install it once, then purge a file:
pip install git-filter-repo
git filter-repo --path .env --invert-paths
If you prefer a jar-based tool, BFG Repo-Cleaner does the same with less rewriting:
bfg --delete-files .env
git reflog expire --expire=now --all && git gc --prune=now --aggressive
Then push the rewritten history:
git push origin --force --all
Two warnings. First, force-pushing rewrites every commit hash, which breaks anyone who cloned the repo — coordinate with collaborators. Second, forks and clones made before the rewrite still hold the old history, and you cannot reach them. That is exactly why revocation, not history rewriting, is the real fix.
Rotate without breaking your app
After you revoke, your app is broken until the new key is in place. Do the replacement through an environment variable, never in source:
- Put the new key in a
.envfile that is gitignored. - Reference it in code with
process.env.STRIPE_SECRET_KEY(or your framework's equivalent). - Redeploy, and confirm the new key works before you delete any backup copy.
The rule is simple: server keys live on the server. Anything that must appear in browser code belongs to a separate, restricted key — like a Supabase anon key that is designed to be public, which only works when your row-level security rules are in place.
The fix prompt for your coding agent
You can hand this whole job to Cursor, Claude Code, or Lovable. Paste this prompt as-is, and it will name the files for you:
Scan this repo for leaked secrets and hard-coded keys, then fix them:
1. Run a secrets scan (gitleaks detect --source .) and list every finding with file path and line.
2. For each finding, move the secret into a .env file, reference it via process.env, and make sure .env is in .gitignore.
3. Rewrite git history to purge the secret from every past commit (git filter-repo --path <file> --invert-paths).
4. Flag clearly any key that was ever committed to a public repo so I can revoke and rotate it in the provider dashboard.
5. Add a pre-commit hook that blocks a commit containing a secret pattern.
Then check that gitleaks detect returns no findings, git log -S '<old-key>' --all returns nothing, and no secret appears anywhere in the working tree.
AI assistants get this wrong in a predictable way: they delete the file but forget the history, or move a key to a client-side variable that still ships to the browser. Vibe coding security risks are mostly these quiet mistakes, which is why the prompt above insists on the "then check that" line.
How to stop leaking keys in the first place
The cheapest leaked API key is the one you never commit. Four habits cover most cases:
- Ignore
.envfrom day one. Put.env,.env.local, and any secrets file in.gitignorebefore the first commit. Add it now if you have not. - Use environment variables everywhere. No key string belongs in source, and no key belongs in a
NEXT_PUBLIC_orVITE_prefixed variable — those are compiled into the browser bundle by design. - Turn on secret scanning. On GitHub, enable it on every repo. On your machine, add a pre-commit hook so a secret never enters a commit at all.
- Scope every key to the minimum. If the provider lets you restrict a key to one product or one bucket, do it. A narrowly scoped key turns a leak into a small problem instead of an account takeover.

Photo by Christopher Gower on Unsplash
A pre-commit hook with gitleaks catches most slips before they leave your machine:
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0 # use the latest release tag
hooks:
- id: gitleaks
Frequently asked questions
What happens if an API key is leaked?
Whoever finds the key can use it exactly as you could, with no login. For a billing key that means charges on your card; for a database key it means reading or deleting data. Bots scan public repos for these strings, so a key pushed publicly is usually abused within minutes.
Why is exposing API keys bad?
An API key is a bearer credential — possession is proof of identity. There is no password check or second factor to stop someone who holds the string. Exposing it is the same as handing over your password, but for a specific and sometimes very powerful capability.
Can I just delete the file that contains the key?
No. Deleting the file and pushing removes the key from the latest version, but every past commit still contains it. On a public repo you must revoke the key at the provider and rewrite history with git filter-repo or BFG.
How do I know if my API key has been leaked?
Check GitHub secret scanning under your repo's security settings, run a local scanner like gitleaks detect --source ., and watch your provider's usage dashboard for a spike. A Rustine quick scan also shows secrets that made it into your live site.
Does revoking a key break my app?
Yes, temporarily. Revoke first to stop the attacker, then immediately add the new key through an environment variable and redeploy. A few minutes of downtime is cheaper than a drained account.
The fastest way to know what is leaking is to look at your app the way an attacker does. Run a free scan on Rustine — it shows the exact request and response that expose each secret, and gives your coding agent the fix to paste. If the app was AI-built, the MCP server lets the agent scan and fix by itself.