Web DevelopmentBackendFrontend

How to Permanently Remove a Committed .env File or API Key from Git History

By Connect Digital World | Developer Guides Made Easy | Last updated: October 2026

Your stomach drops. You just pushed your code to GitHub, and then you notice it: your .env file, with your database password and API keys, is sitting in the repository. You quickly delete the file, commit and push again. Phew, right?

Wrong. The secret is still in your Git history, and anyone can find it. Do not panic. Here is exactly what to do, in the right order, with the exact commands.

Step Zero: Revoke the Key First (Do This Before Anything Else)

This is the most important rule in this whole guide: treat any secret that was pushed to a public repository as stolen. Automated bots scan GitHub all the time looking for exposed keys, and they can find them within minutes. Cleaning your history is important, but it does not un-steal a key.

  1. Revoke or rotate the key right now. Log in to the service (AWS, Stripe, OpenAI, a database, your email provider and so on), delete the exposed key and create a new one.
  2. Change any passwords that were in the file.
  3. Check your logs and billing dashboard for strange activity.
  4. Only then, clean up the Git history.

The Quick Fix: Exact Commands

You have two good tools. Use git-filter-repo (recommended by the Git project) or BFG Repo-Cleaner (very simple). Always work on a fresh clone, and make a backup first.

Option A: git-filter-repo (Recommended)

1. Install it:

pip install git-filter-repo
# or: brew install git-filter-repo
# or (Debian/Ubuntu): sudo apt install git-filter-repo

2. Make a fresh clone and enter it:

git clone https://github.com/YOUR-USER/YOUR-REPO.git
cd YOUR-REPO

3. Remove the .env file from every commit:

git filter-repo --invert-paths --path .env

To remove several files at once:

git filter-repo --invert-paths --path .env --path config/secrets.yml

4. Add your remote back (git-filter-repo removes it as a safety step):

git remote add origin https://github.com/YOUR-USER/YOUR-REPO.git

5. Force-push the cleaned history:

git push origin --force --all
git push origin --force --tags

Option B: BFG Repo-Cleaner

BFG needs Java. Download the bfg.jar file from the official BFG Repo-Cleaner website.

1. Remove the file from the latest commit first. BFG protects your newest commit by default, so the file must already be gone from it:

git rm --cached .env
echo ".env" >> .gitignore
git add .gitignore
git commit -m "Stop tracking .env"
git push

2. Make a mirror clone:

git clone --mirror https://github.com/YOUR-USER/YOUR-REPO.git

3. Run BFG:

java -jar bfg.jar --delete-files .env YOUR-REPO.git

4. Clean up and push:

cd YOUR-REPO.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push

That is the short version. Keep reading to understand why it works, how to remove a key that is inside a file, and the extra steps that people forget.

Why Does “git rm” Not Fix the Problem?

This is the question everyone asks, so let us explain it simply.

Git Is Like a Photo Album, Not an Eraser

Imagine your project is a photo album where every commit is a new photo of your whole project at that moment. When you run git rm .env and commit, you add a new photo that no longer shows the file. But the old photos are still in the album, with the secret clearly visible.

Anyone who has your repository can look at an older photo. You can see this yourself:

# Show every commit that touched .env
git log --all --full-history -- .env

# View the old file's contents from a past commit
git show COMMIT_ID:.env

To rewrite history, you must create a new album where the file never existed. That is what git-filter-repo and BFG do.

Other Places the Secret Can Hide

  • Old commits on every branch.
  • Tags and releases.
  • Forks that people made before you fixed it.
  • Pull requests and cached views on GitHub.
  • Local clones on teammates’ computers and CI servers.
  • Your local reflog, which keeps old commits for a while.

Step-by-Step: The Full Process

Step 1: Revoke and Rotate the Secret

We said it before, but it is worth repeating. Do this first, and keep going afterwards.

Step 2: Find Out Exactly What Leaked

Look for the secret in your history. To search for a specific text, such as part of a key, use:

git log --all -p -S'AKIA' --source

The -S option finds commits where the number of times that text appears changed. Replace AKIA with the beginning of your key.

To scan the whole repository with a secret-scanning tool, you can use a free tool such as Gitleaks or TruffleHog:

gitleaks detect --source . --verbose

This helps you find other secrets you may not know about.

Step 3: Make a Backup and a Fresh Clone

Copy your whole repository folder somewhere safe, or make a mirror backup:

git clone --mirror https://github.com/YOUR-USER/YOUR-REPO.git backup-repo.git

Then work in a separate, fresh clone, as shown above. git-filter-repo is designed for fresh clones and will refuse to run on a repo that looks used, unless you add --force, which you should avoid.

Step 4: Rewrite History

Use one of the two tools from the Quick Fix section.

Step 5: Check That the Secret Is Really Gone

git log --all --full-history -- .env
git log --all -p -S'YOUR_KEY_START' --source

Both commands should print nothing. Also scan again with Gitleaks.

Step 6: Force-Push

Because history changed, a normal push will be rejected. You must force-push:

git push origin --force --all
git push origin --force --tags

If your main branch is protected, you may need to temporarily allow force pushes in the repository’s branch protection settings, then turn the protection back on afterwards.

Step 7: Clean Up GitHub

Rewriting your repository does not remove every trace on GitHub. According to GitHub’s guidance for removing sensitive data:

  • Old commits can still be visible through cached views, pull requests and forks.
  • If the data was sensitive, you can contact GitHub Support to ask them to remove cached views and references to the data in pull requests.
  • Forks are separate copies owned by other people. You cannot rewrite them yourself, so you must treat the secret as exposed. This is another reason rotating the key matters.

Step 8: Tell Your Team

Everyone who cloned the repository must update their copy, or the old history can come back when they push. The safest way is to delete their old local copy and clone again. If they want to keep local work, they can do:

git fetch --all
git reset --hard origin/main

Warning: git reset --hard throws away uncommitted local changes, so save any work first. Also remind everyone not to merge old branches that still contain the secret.

How to Remove a Key That Is Inside a File (Not the Whole File)

Sometimes you did not commit a .env file. You hard-coded a key inside a normal source file, such as app.js or settings.py, and you want to keep the file but erase the secret. Both tools can replace text across all history.

With git-filter-repo

Create a file called replacements.txt with one rule per line. The text before ==> is replaced with the text after it:

AKIAIOSFODNN7EXAMPLE==>***REMOVED***
my-super-secret-password==>***REMOVED***
regex:sk-[A-Za-z0-9]{20,}==>***REMOVED***

If you leave out the ==> part, the text is replaced with ***REMOVED*** by default. Then run:

git filter-repo --replace-text replacements.txt

With BFG

Create a text file called secrets.txt with one secret per line:

AKIAIOSFODNN7EXAMPLE
my-super-secret-password

Then run:

java -jar bfg.jar --replace-text secrets.txt YOUR-REPO.git

Do not commit replacements.txt or secrets.txt to your repo. They contain the secrets themselves! Delete them when you finish.

git-filter-repo vs BFG: Which One Should You Use?

Feature git-filter-repo BFG Repo-Cleaner
Needs Python 3 and Git Java
Ease of use Simple for common jobs, very flexible Very simple, fewer options
Remove a file --invert-paths --path .env --delete-files .env
Replace text --replace-text file --replace-text file
Protects latest commit? No, it rewrites everything Yes, by default
Speed on big repos Fast Fast
Best for Most people and complex cases Quick “delete this file” jobs

What about the old git filter-branch command? The Git project itself warns that it is slow and easy to get wrong, and recommends git-filter-repo instead. Avoid it.

Common Mistakes to Avoid

  1. Cleaning history but not rotating the key. The most dangerous mistake.
  2. Using git rm and thinking you are done. The old commits still hold the secret.
  3. Working in your normal copy. Always use a fresh clone and keep a backup.
  4. Forgetting tags and other branches. Push with --all and --tags.
  5. Forgetting to add the remote again after git-filter-repo.
  6. Not telling teammates. Old clones can reintroduce the secret.
  7. Committing your replacements file. It contains the secret too.
  8. Ignoring forks and pull requests. You cannot clean other people’s copies, so rotate the key.
  9. Panic-deleting the whole repository. Not needed. It does not help with forks or clones that already exist.

How to Never Let This Happen Again

1. Add .env to .gitignore Before Your First Commit

# .gitignore
.env
.env.*
!.env.example

2. Commit an .env.example File Instead

Share a template with fake values, so other developers know which settings they need without seeing real secrets:

DATABASE_URL=postgres://user:password@localhost:5432/mydb
API_KEY=your-api-key-here

3. Use a Pre-Commit Secret Scanner

Tools such as Gitleaks, detect-secrets and TruffleHog can check your code before each commit and stop you if they see a key.

4. Turn On GitHub Secret Scanning and Push Protection

GitHub offers secret scanning, and push protection that can block a push when it detects a known type of secret. Check your repository’s security settings to turn these on, since availability can depend on your plan and repository type.

5. Use a Proper Secrets Manager

For real projects, store secrets in your hosting platform’s secret settings, GitHub Actions secrets, or a secrets manager, rather than in files that travel with your code.

6. Limit What a Key Can Do

Give each key the smallest permissions it needs, set spending limits and use separate keys for development and production. Then a leak does less damage.

Quick Panic Checklist

  1. Revoke and rotate the leaked key.
  2. Check logs and billing for misuse.
  3. Back up the repository.
  4. Make a fresh clone.
  5. Run git-filter-repo or BFG.
  6. Verify the secret is gone from all history.
  7. Re-add the remote (git-filter-repo) and force-push all branches and tags.
  8. Contact GitHub Support if the data was sensitive and cached views or pull requests still show it.
  9. Tell every collaborator to re-clone.
  10. Add .gitignore, a secret scanner and push protection.

Simple Word List (Glossary)

  • Commit: A saved snapshot of your project in Git.
  • History: The list of all past commits.
  • .env file: A file that stores secret settings like passwords and API keys.
  • API key: A secret code that lets a program use a service.
  • Rotate a key: Replace an old key with a new one.
  • git-filter-repo: A tool that rewrites Git history.
  • BFG Repo-Cleaner: A fast tool for removing files and secrets from history.
  • Force push: Pushing that overwrites the remote history.
  • Fork: A copy of a repository made by another user.
  • Mirror clone: A complete copy of a repository, including all branches and tags.
  • Reflog: Git’s private diary of where your branches have been.

Frequently Asked Questions (FAQ)

Does git rm remove a file from Git history?

No. It only removes the file from future commits. The older commits still contain it, so anyone can view it.

How do I remove a .env file from all commits?

Use git-filter-repo: git filter-repo --invert-paths --path .env, or BFG: java -jar bfg.jar --delete-files .env. Then force-push.

Do I need to revoke my API key even after cleaning history?

Yes, always. Bots can copy keys within minutes of a push, and forks or caches may still hold the old commits.

Is deleting the repository enough?

No. Existing forks, clones and cached copies may still have the secret. Rotate the key.

Will force-pushing break my repository?

It replaces the remote history with the rewritten one. Back up first, and tell collaborators to re-clone so they do not push the old history back.

Can I remove a secret without deleting the whole file?

Yes. Use --replace-text with a list of secrets in git-filter-repo or BFG.

Which tool is better, git-filter-repo or BFG?

git-filter-repo is the Git project’s recommended tool and is more flexible. BFG is great for quick, simple jobs.

How can I contact GitHub about cached sensitive data?

Follow GitHub’s documentation on removing sensitive data and contact GitHub Support with details of the repository and the commits.

How do I stop committing secrets in the future?

Use .gitignore, a pre-commit scanner, GitHub push protection, and a secrets manager.

Final Thoughts

Everyone makes this mistake at least once, even experienced developers. What matters is how you react. Revoke the key first, then use git-filter-repo or BFG to rewrite history, force-push the clean version, tell your team and tighten your habits so it does not happen again.

And remember the key lesson: Git remembers everything, so git rm alone is never enough. Fix the history, but trust only a rotated key.

Did this guide help you recover from a leaked key? Tell us in the comments, so others can learn too. For more easy developer guides, keep following Connect Digital World.

Sources and further reading: The git-filter-repo manual, the BFG Repo-Cleaner documentation, GitHub Docs on removing sensitive data from a repository and on secret scanning, and the Git documentation. Always check the current documentation for your version of each tool.


Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button