You just found it. That API_KEY=... has been in your repo's history for six months. Your heart rate goes up. Good — now do these three things in order, because the order is the whole game.
Step 1: Rotate the Secret First (Not Second, Not Later)
This is the step everyone skips, and skipping it makes everything else decorative.
A leaked secret is compromised the moment it is readable. Deleting it from the repo does not un-read it. Bots index public repos. Someone may have already cloned it.
Go to the provider's dashboard and rotate now:
- API key → new key, old key revoked
- Database password → new password, old one gone
- JWT secret → new value (yes, your sessions die; that is cheaper than a breach)
- Webhook token → regenerate
Do this before touching git at all. The clock starts at leak, not at cleanup.
Step 2: Scan for What Else Is Exposed
You found one. Assume there are more. Run a scan across the whole project:
npx @wuchunjie/dotguard .
🔍 Scanning: /home/dev/my-app
📄 .env (2 issues)
⚠️ L 3 | API key
⚠️ L 7 | Database URL
DotGuard walks every .env file and flags passwords, keys, tokens, private keys, and database URLs — with line numbers. Zero dependencies, one command.
Rotate everything it finds, not just the one that got you caught.
Step 3: Clean the History
The secret is in history, so history is the leak. Your options, in order of how much you care about the history:
Option A: Small repo, no precious history — delete and re-clone.
# from a clean checkout without the bad commits
git init
git add .
git commit -m "fresh start"
git push --force
Nuclear, but honest. For side projects this is the fastest correct answer.
Option B: Shared repo, history matters — rewrite with git filter-repo, remove the file, force-push, and make every clone re-clone (a filtered repo's history is unrecoverable from stale clones — the old secret lives on in every machine that ever cloned it).
Option C: The secret was short-lived — if it was rotated within minutes of the push, document the incident and move on. Risk is time-exposed × value-of-secret.
Aftermath: Make It Not Happen Again
-
.envin.gitignore,.env.examplecommitted with empty values - A pre-commit hook that runs the scanner (its non-zero exit blocks the commit)
- The same scan as a CI step
Three gates, all free, all one-liners. The incident you are cleaning up right now is the cheapest lesson it will ever cost you.
npm: @wuchunjie/dotguard | GitHub: wuchunjie00/devtools
From the same toolbox
-
ScaffoldX — generate production-ready project templates in seconds:
npx scaffoldx-cli -
DotGuard — scan
.envfiles for exposed secrets:npx @wuchunjie/dotguard -
GitPulse — git analytics (commits, contributors, activity) in your terminal:
npx @wuchunjie/gitpulse -
SnippetX — save, search, and copy code snippets from the terminal:
npx @wuchunjie/snippetx
☕ If This Saved You Time
All of these tools are and will always be 100% free. If they make your day a little easier, consider fueling the next one:
Built with ❤️. Zero dependencies, zero tracking, zero bloat.
Top comments (0)