First: breathe. You’re not the first, and it’s fixable.
You pushed. You looked at the diff a second too late. There it is, sitting in a public commit: an API key, a .env that wasn’t supposed to ship, a database URL with the password right in it.
If you’re feeling a little anxious right now, here’s the good news; that reflex is going to get this fixed fast. This happens to everyone, including people who’ve been shipping code for twenty years. It’s not a character flaw, it’s a random Tuesday. Let’s just deal with it, in the right order, without panicking.
One thing to get out of the way, because it changes everything you’re about to do:
Deleting the commit does not fix this.
The moment a secret lands in a public commit, assume it’s already been copied. Bots scrape the public commit stream continuously (that’s literally how tools like this one work), and a leaked key can be found and used within minutes. The objective isn’t to conceal it-that opportunity has passed. The goal is to make the leaked key useless.
Here’s the order that actually matters.
Step 1 — Rotate the key. Right now. Before anything else.
This is the only step that truly closes the hole. Everything else is cleanup.
Rotating means: generate a new key, and revoke/disable the old one so the leaked value stops working. Do this before you touch git history : a scrubbed history with a still-valid key out in the wild is a false sense of safety!
Go straight to the provider and kill the exposed key:
- AWS → IAM → Users → Security credentials → deactivate then delete the access key. (If it was a root key, rotate it and turn on MFA while you’re there.)
- Stripe → Developers → API keys → Roll key.
- OpenAI / Anthropic → API keys page → revoke, create a new one.
- GitHub token (PAT) → Settings → Developer settings → Tokens → delete it.
- Google Cloud → APIs & Services → Credentials → delete the key, create a new one, and add restrictions this time.
- A database password → change the password / rotate the credential on the DB user immediately. If the connection string was public, treat the whole DB as exposed (yeah i know that’s terrible).
Don’t have time to figure out which provider it is or how bad it is? That’s exactly the kind of thing a quick scan will tell you: the key type, whether it’s still live, and where to rotate it. But if you already know what it is, just go revoke it. Come back after.
Step 2 — Remove it from the git history
Now that the key is dead, clean up so you’re not leaking a pattern of secrets and so nobody trips over it later. Remember: removing it from the latest commit isn’t enough it’s still sitting in every commit in between!
Two solid tools for this. Pick one.
git filter-repo (the modern, recommended one):
# install: pip install git-filter-repo
git filter-repo --path path/to/leaked/file --invert-paths
Or, to scrub a specific string everywhere it appears:
git filter-repo --replace-text <(echo 'SUPER_SECRET_VALUE==>REMOVED')
BFG Repo-Cleaner (simple and fast for this exact job):
bfg --replace-text passwords.txt # passwords.txt lists the strings to strip
git reflog expire --expire=now --all && git gc --prune=now --aggressive
Then force-push:
git push --force
Two honest caveats:
- Force-pushing rewrites history. If other people work on this repo, warn them first, they’ll need to re-clone or reset, or you’ll create a mess.
- Forks and clones still have the old history. You can’t reach those. This is another reason Step 1 (rotate) is the one that actually protects you, not this step.
Step 3 — Check whether the key was already used
The key is dead and the history is clean. Last thing: make sure nobody had a party on your account in the meantime.
- AWS → CloudTrail → look for API calls you don’t recognize, especially new IAM users, EC2 instances in weird regions, or billing spikes. Check the Billing dashboard — crypto-mining on a leaked key is the classic.
- Stripe / payment keys → the events/logs timeline for requests you didn’t make.
- OpenAI / Anthropic / any usage-billed API → the usage dashboard. A sudden spike = someone else spent your quota.
- GitHub token → your account’s security log for actions you didn’t take.
If something looks off, rotate everything adjacent, not just the one key, and check whether that key could reach other systems.
That’s it. Seriously.
Rotate → clean → check. Nail these three, and the leak goes from “uh-oh” to “all sorted”. No need to spiral about it.
If there’s one habit worth taking from this, it’s not “never make mistakes”, that’s not a real plan. It’s shortening the time between leak and you finding out. A key that’s caught and rotated in five minutes is a non-event. The same key sitting unnoticed for three days is the horror story.
That gap is the whole reason LeakWatch exists: it watches your repos on the public forges and pings you the second a secret shows up, so you’re doing Step 1 while the key is still warm instead of reading about it in a billing alert. The heads-up is free — you can point it at a repo and see for yourself in about 30 seconds.
But honestly? Whether you use it or not, if you did the three steps above, you’re good. Go drink a beer 🙂