On this page
How to Find and Remove Secrets From Git History
Deleting a secret from your code doesn't remove it from git history. Find leaked keys with gitleaks, scrub history, and rotate what leaked.
Quick answer
- A committed secret stays in git history forever, even after you delete it in a later commit.
- Find leaks with gitleaks or trufflehog; scrub history with git filter-repo; then force-push and rotate the secret.
- The leak is only truly closed when the secret is rotated — scrubbing history is cleanup, not security.
Why doesn’t deleting the file fix the leak?
Git stores every version of every file. A commit that removes a secret is just another commit on top of the one that introduced it; the old blob remains reachable through history and through any clone or fork made since. Anyone with repo access — or who got the repo in a bundle or fork — can run git log and recover the key. The deletion commit gives a false sense of closure: the secret is still live and still leaked.
How do I find what leaked?
Scan the repo with a tool built for this. Gitleaks scans the working tree and full history for high-entropy strings and known patterns (AWS keys, GitHub tokens, private keys), with an allowlist file to manage false positives. Run it once against the whole history to build the inventory of what leaked, when, and where — that inventory tells you what must be rotated, which is the part that actually matters.
# Scan the entire history for secrets\ngitleaks detect --source . --log-opts="--all" --report-path leaks.json\n# Review leaks.json: each entry has the secret, file, and commit.How do I scrub the history?
Use git filter-repo (the modern replacement for filter-branch) to remove the offending files or replace the strings across all history. This rewrites every commit, so all collaborators must re-clone, and any open PRs, forks, or CI caches will still contain the old history — you can’t un-leak a secret, only stop the bleeding. After the rewrite, force-push to the remote and tell everyone to re-clone. Then, the critical step: rotate the leaked secret at the provider, because it was exposed.
# Remove a file from all of history\ngit filter-repo --invert-paths --path .env\n# Replace a specific string everywhere (less thorough — prefer removal)\ngit filter-repo --replace-text replacements.txt\n\n# Then: force-push, have everyone re-clone, and ROTATE the secret.Where this bites vibecoders
The signature AI incident: a demo push contains a real API key, someone spots it, the developer deletes the line and pushes ‘fixed’. The key is still in history, still valid, still indexed. The assistant never warns about history — it happily rewrites the current file and calls it done. The correct sequence — scan, scrub, re-clone, rotate — is a short runbook that converts a ‘fixed’ leak into an actually closed one.
Where AI coding assistants get this wrong
- Telling you that deleting the line or file fixes the leak.
- Suggesting filter-branch (deprecated) instead of filter-repo.
- Scrubbing history but skipping rotation, leaving the key live.
- Force-pushing without coordinating re-clones, so old history persists in forks.
Checklist
- Scan full history with gitleaks and inventory every leaked secret.
- Scrub history with git filter-repo (remove files or replace strings).
- Force-push and have all collaborators re-clone; drop stale forks and caches.
- Rotate every leaked secret at the provider — this is the security fix, not the rewrite.
FAQ
Is force-pushing after a history rewrite safe?
It’s the only way to propagate the rewrite, but it disrupts everyone: existing clones diverge, open PRs break, and CI caches keep old blobs. Plan it: announce, rewrite, re-clone. On shared branches this is disruptive enough that many teams prefer rotation plus a fresh secret over rewriting history.
Can I remove secrets from history on GitHub?
GitHub Support can purge a specific commit or path from its caches and forked copies for public repos. For private repos and self-hosted remotes, you handle the rewrite yourself. Either way, rotation remains the only complete fix for the exposed secret.
Related topics
- How to Scan Your Codebase for Hardcoded Secrets
- How to Manage Secrets and Environment Variables Properly
- Why Do .env Files Keep Leaking Secrets?
- How to Automate API Key Rotation
- What the Moltbook Breach Teaches About Shipping Vibecoded Apps