How to Remove Secrets from Git History
Cleaning up after accidental commits
TL;DR
TL;DR: First, rotate the compromised credential. Then use BFG Repo Cleaner to remove the secret from all commits. Force push the cleaned history. Alert team members to re-clone. Remember: if the secret was ever pushed to a public repo, consider it compromised regardless of cleanup.
Critical First Step: Rotate the Credential
Always rotate (replace) the exposed secret BEFORE cleaning git history. Once a secret is pushed, especially to a public repo, assume it's been scraped. Cleaning history prevents future exposure but doesn't undo potential current compromise.
Method 1: BFG Repo Cleaner (Recommended)
BFG is faster and simpler than git filter-branch. It's designed specifically for removing unwanted data.
Install BFG
# macOS
brew install bfg
# Or download the JAR file
# https://rtyley.github.io/bfg-repo-cleaner/
Clone a fresh copy
# Clone with --mirror for full history
git clone --mirror git@github.com:you/your-repo.git
Create a file with secrets to remove
# secrets.txt - one secret per line
sk_live_abc123xxxxx
AKIA1234567890xxxxx
ghp_xxxxxxxxxxxx
Run BFG to remove secrets
# Remove specific strings from all history
bfg --replace-text secrets.txt your-repo.git
# Or remove entire files
bfg --delete-files .env your-repo.git
bfg --delete-files "*.pem" your-repo.git
Clean up and push
cd your-repo.git
# Clean up the repo
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# Force push
git push --force
Method 2: git filter-repo
A newer, Python-based alternative to filter-branch:
# Install
pip install git-filter-repo
# Remove a file from all history
git filter-repo --invert-paths --path .env
# Replace text in all files
git filter-repo --replace-text expressions.txt
Create expressions.txt:
# Format: literal:old==>new or regex:pattern==>replacement
literal:sk_live_abc123==>***REMOVED***
regex:sk_live_[a-zA-Z0-9]+==>[REDACTED]
Method 3: git filter-branch (Legacy)
The traditional method, slower but works without additional tools:
# Remove a specific file from all commits
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch .env" \
--prune-empty --tag-name-filter cat -- --all
# Clean up
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# Force push all branches
git push origin --force --all
git push origin --force --tags
Warning: Force Push Required
Rewriting history requires force pushing. This will cause issues for anyone who has cloned the repo. All team members will need to re-clone or carefully rebase their local copies.
After Cleaning: Team Coordination
- Notify your team that history was rewritten
- Ask everyone to delete and re-clone the repository
- Delete any forks that might still contain the secret
- Check CI/CD caches for cached copies
- Request GitHub to clear caches if it was a public repo
For GitHub: Contact Support
If secrets were in a public repo, contact GitHub Support to clear their caches:
- Go to GitHub Support
- Select "Sensitive data removal"
- Provide the repository and commit details
Verify Secrets Are Removed
# Search all history for the secret
git log -p --all -S "sk_live_abc123" --source
# If no results, the secret is removed from all commits
# Also check for the file
git log --all --full-history -- .env
Do I really need to clean history if the repo is private?
It's still recommended. Private repos can become public, team members change, and the secret sits in history indefinitely. However, rotating the credential is the priority. History cleaning can be secondary for private repos.
Will this affect open pull requests?
Yes, open PRs will have conflicts after history rewrite. They'll need to be rebased or closed and reopened against the new history.
Can someone still see the secret from before I cleaned it?
If anyone cloned the repo before cleanup, they have the secret in their local history. GitHub and other hosts may also have cached copies. This is why rotating the credential is essential.
Related guides:How to Rotate API Keys · How to Gitignore Secrets · How to Enable Secret Scanning