Rotate the key first. Do not start by deleting the commit. Deleting it feels like the fix and is not one: the key stays in your repository's history, in every clone and fork, and in caches that scrape public commits within seconds of a push. The only action that actually helps is making the old key worthless.
The next twenty minutes, in order
1. Rotate it now (2 minutes). Go to the provider — OpenAI, Anthropic, Stripe, AWS — create a new key, and revoke the old one. Do this before anything else, before you understand how it happened, before you tell anyone. Every minute the old key is valid is a minute someone can spend your money.
2. Check what it was used for (5 minutes). Provider dashboards show usage. Look for calls you did not make, from regions you have never been in. For a cloud key, check for resources you did not create — crypto mining on a stolen AWS key is automated and starts within minutes.
3. Update wherever the key lived (5 minutes). Your .env,
your hosting provider's environment variables, your CI secrets. The app is
broken until you do, which is a useful forcing function.
4. Now deal with the history (5 minutes). Add the file to
.gitignore. If the repository is private and has few clones, you
can rewrite history with git filter-repo — but treat this as
tidying, not remediation. The remediation was step 1.
5. Stop the next one (3 minutes). Turn on your host's secret
scanning — GitHub's is free and on by default for public repositories. Add a
pre-commit hook, or simply make sure .env is in
.gitignore before your next commit rather than after.
Why deleting the commit is not enough
Three reasons, all of which have caught people out:
Git keeps history. The commit that added the key still exists in the repository unless you rewrite history, and anyone who cloned before you fixed it has it regardless.
Forks are separate copies. You do not control them and cannot delete from them.
Scrapers are faster than you. Bots watch the public commit firehose continuously. Published keys are typically found in under a minute — long before you notice.
How much it can cost
A leaked cloud key is used for crypto mining, billed to you, usually within hours. A leaked model API key is used until the quota runs out. A leaked Stripe secret key can read your customers and issue refunds.
The reason to rotate before investigating is that these are all automated. You are not racing a person deciding whether to bother.
Questions people ask
The repo is private. Am I fine? Better, not fine. Private repositories become public by accident, get shared with contractors, and are cloned to laptops. Rotate anyway.
It was only a publishable key. Then you are fine. Stripe's
pk_ and Supabase's anon key are designed to be
public. Secret keys, database URLs and provider tokens are not.
How do I know if I have others? Search your whole project history,
not just current files — and check anything with NEXT_PUBLIC_,
VITE_ or REACT_APP_ in front of it, because that
prefix compiles the value into the JavaScript every visitor downloads.
Finding them before they leak
npx launchprep reads your code and your git history for
exposed keys, publishable-versus-secret confusion, and the browser-prefix
trap. Free, unlimited, and it runs on your machine — nothing is uploaded.
The wider pre-launch list is here, and if you are on Supabase, the Lovable and Supabase checks cover the settings that leak data without a key being involved at all.