Emergency Recovery Guide: Accidentally Pushed .env or API Keys to Git
Just ran git push and realized your .env or AWS_SECRET_ACCESS_KEY went up with it? Your heart just dropped. What you need right now is not panic — it is first aid you can follow in order. Stick to the sequence below.
Do this first — priority order
- (First) Immediately revoke/rotate the exposed keys and passwords — deletion comes after
- Completely remove secrets from git history, then force push
- Notify collaborators + set up prevention
The order might feel backwards. Key invalidation comes before rewriting history. Here is why.
Trap 1 — git rm --cached plus a new commit will never actually remove it
This is the most common mistake.
git rm --cached .env
git commit -m "remove .env"
git pushThat removes .env from the current file tree. But git permanently stores every change as commit objects. The secret is still frozen in earlier commits. Check for yourself.
# Past commits still print the full .env contents
git log -p -- .env
# Or check out that commit and the file comes back
git checkout <commit-with-secret> -- .env
cat .env # the password is sitting there intactThe "deletion commit" actually becomes a billboard saying a secret used to live here — which makes things worse. Bottom line: you do not need a new commit, you need to rewrite history itself.
Completely purge history: git filter-repo vs BFG Repo-Cleaner
Both tools strip files/strings out of history entirely. Before you start, take a backup clone (git clone --mirror).
Method A — git filter-repo (recommended)
# Install
pip install git-filter-repo # or brew install git-filter-repo
# Remove a specific file from all of history
git filter-repo --path .env --invert-paths
# Keep the file, mask only the values
# patterns.txt example: AKIA[0-9A-Z]{16}==>***REMOVED***
git filter-repo --replace-text patterns.txtMethod B — BFG Repo-Cleaner (large repos, simpler)
# After downloading
java -jar bfg.jar --delete-files .env
# Or replace specific strings (list tokens to remove in secrets.txt)
java -jar bfg.jar --replace-text secrets.txt
# BFG requires you to run post-processing yourself
git reflog expire --expire=now --all
git gc --prune=now --aggressivefilter-repo auto-cleans .git/refs/original, but it is still a good idea to run reflog and gc with either tool.
| Item | git filter-repo | BFG Repo-Cleaner |
|---|---|---|
| Speed | Fast | Very fast (large repos) |
| Flexibility | Very high (paths, regex, etc.) | Mostly simple files/strings |
| Dependencies | Python | Java (JVM) |
| Best for | When you need fine-grained control | Quickly cleaning huge repositories |
force push
git push --force-with-lease --all
git push --force-with-lease --tagsUse --force-with-lease instead of plain --force. It prevents accidentally overwriting work someone else pushed in the meantime.
The most important step — exposed keys are not "deleted", they are "revoked"
Core principle: assume a bot scanned it the moment you pushed. Public repos get crawled by automated scanners within seconds. Private does not mean safe either. No matter how clean you make history, a key someone may already have copied is worthless unless you invalidate it.
Where to revoke, by service:
- AWS: IAM → Access keys → Deactivate then Delete the key, issue a new one
- OpenAI: Platform → API keys → Revoke, then reissue
- Stripe: Dashboard → Developers → API keys → Roll/Reveal
- DB passwords: Change immediately and sync app config / secret manager
- GitHub PAT/OAuth: Settings → Developer settings → delete the token
GitHub Secret Scanning detects some partner tokens (e.g. GitHub Token, AWS, Stripe), notifies the issuer, and sometimes auto-revokes the token. Do not ignore those alerts. Since 2024, Push Protection is on by default for public repos and can block the push itself.
A note from the field: Early in my career I figured a private repo was fine and delayed rotating keys. A token that lingered on a forked internal mirror blew up a month later. "It's private, I'll get to it" is the most expensive assumption you can make.
Impact on collaborators — deleting is not the end
After you rewrite history, other teammates still have the old history (secrets included) locally. Also:
- Open PRs / cached diffs: GitHub PR pages can cache old commit diffs → close and recreate the PR if needed
- forks: old objects still live in forked repos
Send the team something like this:
"We rewrote history. Throw away your existing clone and clone fresh. Do not rebase/merge from your local copy. Related keys have already been revoked and reissued."
Wrap-up — a 3-piece prevention kit
Set these up today so you never go through this twice.
# 1. Add to .gitignore
echo ".env" >> .gitignore
echo "*.pem" >> .gitignore
# 2. Install and register git-secrets
brew install git-secrets
git secrets --install
git secrets --register-aws
git secrets --add 'AKIA[0-9A-Z]{16}'
# 3. Block at push time with a pre-commit hook
pip install pre-commit detect-secretsExample .pre-commit-config.yaml:
repos:
- repo: https://github.com/Yelp/detect-secrets
rev: v1.5.0
hooks:
- id: detect-secretsAdd gitleaks or trufflehog in CI for another filter before merge. These days AI coding assistants often auto-fill example keys that then get committed as-is — another reason to keep automated detection on.
Apply-today checklist
- Rotate/revoke every exposed key
- Change DB and service passwords
- Clean history with filter-repo/BFG, then
--force-with-leasepush - Tell the team to re-clone; check forks and PRs
- Set up
.gitignore+ git-secrets + pre-commit
FAQ
Q. It's a private repo — do I still have to revoke the keys? A. Yes. Collaborators, forks, CI logs, caches — lots of leak paths, and permissions can change anytime. "I deleted it so we're safe" is a dangerous assumption. Always rotate.
Q. git filter-repo or BFG?
A. Need fine-grained path/regex control? filter-repo. Need to quickly clean a multi-GB repo? BFG is easier. After either, finish with reflog expire + gc --prune=now.
Q. I force-pushed but teammates still have the old secrets on their machines. Now what? A. History rewrite only changes the remote. Tell them to delete the old clone and clone again. More importantly: if you already revoked the keys, leftover copies cannot actually do damage.
Nodelog는 모든 콘텐츠의 내용과 출처를 공개 전에 검토합니다. 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서와 함께 확인하며, 검토 기준과 정정 원칙은 편집 정책에서 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
Comments
Be the first to comment.