Committed a secret to Git? Rotate first, then clean up
Updated
You committed a .env file, a hard-coded password or a private key. The instinct is to delete it and commit again. That removes it from the latest version of the code, but the secret is still in the repository’s history, where anyone with a clone can find it with one command.
1. Rotate the secret first
This is the only step that makes the leaked value useless. If the repository is public, or was ever pushed anywhere others can read, assume the secret has been copied. Revoke it and issue a new one before doing anything else; see the leaked API key checklist.
If the commit is still only on your machine and was never pushed, you may not need to rotate, but be honest with yourself about where it has been.
2. If it was never pushed: amend or reset
For the most recent commit:
git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit --amend --no-edit
For an older local commit, an interactive rebase that edits that commit works too. Either way, check the history afterwards with git log -p and search for the value.
3. If it was pushed: rewrite history
Use git filter-repo, the tool Git itself recommends over the old filter-branch. Work on a fresh clone:
git clone [email protected]:you/repo.git repo-clean
cd repo-clean
To remove a whole file from every commit:
git filter-repo --invert-paths --path .env
To replace a value wherever it appears, list it in a file (one per line), then run:
git filter-repo --replace-text ../secrets-to-remove.txt
Each matching value is replaced with ***REMOVED***. Check the result with git log -p. filter-repo removes the origin remote as a safety measure, so add it back, then force-push every branch and tag:
git remote add origin [email protected]:you/repo.git
git push origin --force --all
git push origin --force --tags
Rewriting history changes commit IDs, so everyone with a clone needs to re-clone (or carefully rebase). Tell your team before you do it.
4. Copies you can’t rewrite
Even after a force-push, the old commits may survive in:
- forks and other people’s clones;
- open or closed pull requests, which keep references to their commits;
- CI logs, caches and build artefacts;
- the hosting provider’s caches. On GitHub, contact support to remove cached views and pull request references.
This is why step 1 matters most. History rewriting tidies up; rotation is what protects you.
5. Stop it happening again
- Add
.env, key files and local config to.gitignorebefore the first commit. - Run a secret scanner such as gitleaks as a pre-commit hook, and in CI.
- Turn on secret scanning and push protection in GitHub or GitLab to block pushes that contain known key formats.
- Keep secrets in your CI’s secret store or a secrets manager, never in the repository.
When you need to share a config file to get help, share a redacted copy instead.