← Back to postsCoding Notes
EnglishPublished Apr 19, 2026Updated Apr 19, 20263 min read

How to Scrub Leaked Secrets from Git History: A Complete Guide

Tips

Accidentally committing a secret (API key, password, or token) is a rite of passage for developers. However, simply deleting the secret in a new commit isn't enough—it remains in your Git history forever.

To truly secure your repository, you must rewrite the timeline. This guide covers the three best ways to do it, depending on your needs.


Step 0: Revoke the Token (Non-Negotiable)

Before you start fixing Git, assume the secret is already compromised.

  1. Revoke/Delete the secret at the source (Bitbucket, AWS, Stripe, etc.).
  2. Generate a new one, but do not hardcode it. Keep it aside for your .env file.

Method 1: The "Professional" Choice (git-filter-repo)

This is the officially recommended tool for rewriting history. It is precise and removes the entire history of a specific file.

1. Installation

On modern Linux (Ubuntu 24.04+), avoid pip. Use apt:

bash
sudo apt update && sudo apt install git-filter-repo

2. Run the Scrub

Go to your project root and run:

bash
git filter-repo --path path/to/your/file.js --invert-paths
  • Result: The file and its entire history are wiped from the repository.

3. Restore Cleanly

Because the file is now gone, recreate it using Environment Variables:

javascript
const TOKEN = env.BITBUCKET_TOKEN; // Safe

Then commit:

bash
git add .
git commit -m "chore: restore file with safe env variables"

Method 2: The "Fast Text Replacer" (BFG Repo-Cleaner)

If you want to keep your file but simply want to replace every instance of the secret string with ***REMOVED*** across all commits, BFG is the fastest tool.

1. Setup

Download the bfg.jar and create a file named passwords.txt. Put your secret strings inside it (one per line).

2. Run the Replacer

bash
java -jar bfg.jar --replace-text passwords.txt .
  • Result: Your history remains intact, but your secrets are replaced by a placeholder string in every old commit.

Method 3: The "Total Reset" (Solo Developer Shortcut)

If you are the only developer and you don't care about keeping your commit history (messages/dates), this is the simplest way to "nuke" the history and start fresh.

1. Remove Git

bash
rm -rf .git

2. Re-initialize

Make sure your files are clean (secrets moved to .env), then:

bash
git init
git add .
git commit -m "Initial clean commit"

Step 2: Overwriting the Remote (The Force Push)

Regardless of the method used, your local history now contradicts the "dirty" history on GitHub or Bitbucket. You must force the remote to accept your clean version.

Note: If you used Method 1 or 3, you must re-add your remote first:

bash
git remote add origin https://github.com/username/repo.git

Then Force Push:

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

Step 3: Syncing Other Machines

If you have this repo on another machine, DO NOT PULL. If you pull, you will merge the dirty history back into your clean history.

On the other machine, run a Hard Reset:

bash
# 1. Download the new clean history
git fetch origin

# 2. Force the local branch to match the clean remote
git reset --hard origin/main

# 3. Clean up the internal cache
git gc --prune=now --aggressive

Final Security Check & Prevention

1. Use a .gitignore

Never let your .env file reach Git. Create a .gitignore in your root:

text
# .gitignore
.env
node_modules/
dist/

2. Architecture: Passing env

Ensure your functions are designed to receive configuration from the environment, especially in serverless environments like Cloudflare Workers:

javascript
// bitbucket-poll.js
export async function pollBitbucket(env) {
  const token = env.BITBUCKET_TOKEN; 
  // ...
}

3. Use Secret Scanning

Enable "Secret Scanning" in your GitHub/Bitbucket settings. These platforms will now block pushes if they detect a known API key format, saving you from having to rewrite history ever again.


Summary:

  • Method 1 (git-filter-repo) is best for deleting specific files.
  • Method 2 (BFG) is best for replacing specific strings.
  • Method 3 (Total Reset) is best for starting fresh on solo projects.