Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to remove sensitive data from Git history

Accidentally committing API keys, passwords, or private certificates to a Git repository is a common security incident, and simply deleting the file in a new commit does not remove the credentials from history — they remain visible in every previous commit. As the creator of CoreUI with 25 years of open-source development experience, I’ve dealt with this exact scenario in public repositories and the fix requires rewriting the entire repository history. The fastest modern tool is git filter-repo, which rewrites all commits to remove the sensitive file in seconds. Immediately rotate any exposed credentials after cleaning history — assume they have been compromised from the moment they were pushed.

The immediate priority when sensitive data is discovered: rotate credentials first, then clean history.

# STEP 1 - Immediately revoke/rotate the exposed credential
# (Before doing anything else - assume it was compromised)
# Rotate in: AWS Console, GitHub Settings, Stripe Dashboard, etc.

# STEP 2 - Clone a fresh copy to work on
git clone --no-local your-repo clean-copy
cd clean-copy

Do not skip step 1. Cleaning history takes time. Rotating the credential is immediate and stops the damage now.

Remove the File with git filter-repo

# Install if needed
pip install git-filter-repo
# or: brew install git-filter-repo

# Remove the specific file from all commits
git filter-repo --path .env --invert-paths
git filter-repo --path config/secrets.yml --invert-paths

# Remove files by pattern (all .env files)
git filter-repo --path-glob '*.env' --invert-paths

# Replace the actual secret value everywhere it appears
git filter-repo --replace-text <(echo 'ACTUAL_SECRET_VALUE==>REMOVED')

--replace-text scans all commit content and replaces matching strings. Create a replacements file with one literal_value==>replacement per line. This catches cases where the secret was copy-pasted into other files.

Remove with BFG Repo Cleaner (Alternative)

BFG is faster for very large repositories.

# Download BFG
curl -o bfg.jar https://repo1.maven.org/maven2/com/madgicaltechdom/bfg/bfg/1.14.0/bfg-1.14.0.jar

# Remove a specific file from history
java -jar bfg.jar --delete-files .env

# Replace all occurrences of a secret string
echo 'ACTUAL_SECRET_VALUE' > secrets.txt
java -jar bfg.jar --replace-text secrets.txt

# Clean up
git reflog expire --expire=now --all
git gc --prune=now --aggressive

BFG operates on your already-checked-out repo (unlike filter-repo which prefers a fresh clone). It’s generally faster for very large repos but has fewer options than filter-repo.

Push and Notify Collaborators

Force-push the rewritten history to all remotes.

# Re-add remote if filter-repo removed it
git remote add origin https://github.com/user/repo.git

# Push all branches and tags
git push origin --force --all
git push origin --force --tags

After force-pushing, all collaborators must re-clone the repository. Their existing clones contain the old history with the sensitive data still present. Contact GitHub/GitLab support to clear cached data in pull request diffs and search indexes.

Best Practice Note

After cleaning history, add the sensitive file patterns to .gitignore immediately to prevent re-committing them. Set up pre-commit hooks with tools like detect-secrets or git-secrets to scan staged files for credential patterns before commits. For private repositories, also audit who has cloned or forked the repo during the exposure window — they may have cached the sensitive history locally. See how to rewrite Git history with git filter-repo for more general history rewriting techniques.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author