DEV Community

ke jia
ke jia

Posted on

Found a Secret in Git History? A Calm 3-Step Recovery Plan

You just found it. That API_KEY=... has been in your repo's history for six months. Your heart rate goes up. Good — now do these three things in order, because the order is the whole game.

Step 1: Rotate the Secret First (Not Second, Not Later)

This is the step everyone skips, and skipping it makes everything else decorative.

A leaked secret is compromised the moment it is readable. Deleting it from the repo does not un-read it. Bots index public repos. Someone may have already cloned it.

Go to the provider's dashboard and rotate now:

  • API key → new key, old key revoked
  • Database password → new password, old one gone
  • JWT secret → new value (yes, your sessions die; that is cheaper than a breach)
  • Webhook token → regenerate

Do this before touching git at all. The clock starts at leak, not at cleanup.

Step 2: Scan for What Else Is Exposed

You found one. Assume there are more. Run a scan across the whole project:

npx @wuchunjie/dotguard .
Enter fullscreen mode Exit fullscreen mode
  🔍  Scanning: /home/dev/my-app

  📄  .env (2 issues)
    ⚠️  L  3 | API key
    ⚠️  L  7 | Database URL
Enter fullscreen mode Exit fullscreen mode

DotGuard walks every .env file and flags passwords, keys, tokens, private keys, and database URLs — with line numbers. Zero dependencies, one command.

Rotate everything it finds, not just the one that got you caught.

Step 3: Clean the History

The secret is in history, so history is the leak. Your options, in order of how much you care about the history:

Option A: Small repo, no precious history — delete and re-clone.

# from a clean checkout without the bad commits
git init
git add .
git commit -m "fresh start"
git push --force
Enter fullscreen mode Exit fullscreen mode

Nuclear, but honest. For side projects this is the fastest correct answer.

Option B: Shared repo, history matters — rewrite with git filter-repo, remove the file, force-push, and make every clone re-clone (a filtered repo's history is unrecoverable from stale clones — the old secret lives on in every machine that ever cloned it).

Option C: The secret was short-lived — if it was rotated within minutes of the push, document the incident and move on. Risk is time-exposed × value-of-secret.

Aftermath: Make It Not Happen Again

  • .env in .gitignore, .env.example committed with empty values
  • A pre-commit hook that runs the scanner (its non-zero exit blocks the commit)
  • The same scan as a CI step

Three gates, all free, all one-liners. The incident you are cleaning up right now is the cheapest lesson it will ever cost you.

npm: @wuchunjie/dotguard | GitHub: wuchunjie00/devtools


From the same toolbox

  • ScaffoldX — generate production-ready project templates in seconds: npx scaffoldx-cli
  • DotGuard — scan .env files for exposed secrets: npx @wuchunjie/dotguard
  • GitPulse — git analytics (commits, contributors, activity) in your terminal: npx @wuchunjie/gitpulse
  • SnippetX — save, search, and copy code snippets from the terminal: npx @wuchunjie/snippetx

GitHub: wuchunjie00/devtools


☕ If This Saved You Time

All of these tools are and will always be 100% free. If they make your day a little easier, consider fueling the next one:

Buy me a coffee on Ko-fi

Built with ❤️. Zero dependencies, zero tracking, zero bloat.

Top comments (0)