Deleting the key from the file does nothing. It is still in your git history
Removing a key from a file and committing the change leaves the key in the previous commit, which is in every clone and every fork forever. Rotate the credential first, because that is what actually ends the exposure. Then rewrite history if the repository is public, and add a pre-commit scanner so it cannot happen twice.
GitGuardian counted 28.65 million new hardcoded secrets in public GitHub commits during 2025, a 34% rise on the year before and the largest single-year jump they have recorded. The number that should worry you more is the follow-up: 64% of those credentials were still valid and unrevoked when they checked again in January 2026.
That second figure is the whole problem in one line. Finding a leaked secret is not hard and lots of people do it. Ending the exposure is a different action, and most people perform the one that feels like it and is not.
Why does deleting the line not work?
Git stores every version of every file. A commit that removes a key does not remove the commit that added it; it adds a new one on top, and the old blob is still reachable by hash, still in every clone anyone made, and still in every fork, mirror and CI cache that ever pulled the repository.
For a public repository it is worse than that. GitHub's event stream is monitored continuously by both researchers and attackers, and published measurements put the time from a secret appearing to somebody trying it in the low minutes. By the time you notice, the question is not whether it was seen but whether it was used.
The practical consequence is that history rewriting is cleanup, not remediation. It is worth doing and it does not undo anything, because the copies are already elsewhere.
What do I do first?
Rotate. Not after you understand how it happened, not after you have cleaned the history, not after work. The credential is burned and the only action that changes anything is issuing a new one and invalidating the old.
The order that works under pressure:
- Issue the replacement and deploy it, so the outage window is short.
- Revoke the old credential at the provider. Deleting the key from your code is not revocation; deleting it from the provider's dashboard is.
- Read the provider's audit log for the period the key was exposed. Stripe, AWS, OpenAI and GitHub all keep one, and it is the only way to answer whether anything was actually done with it.
- Then deal with the repository.
Step three is the one people skip and the one that determines whether this is an incident or an inconvenience. A key that was exposed for six weeks and never used is a lucky escape. A key with API calls from an address you do not recognise is a breach with notification obligations attached.
How do I find what is already in there?
You need to search history rather than the working tree. A grep over your checkout only sees the current version of every file, which is exactly the version you already cleaned.
Gitleaks reads the whole history and is the usual starting point: gitleaks detect --source . --redact will walk every commit and report what it finds with the value masked. TruffleHog does the same job with live-credential verification, which is genuinely useful because it separates a key that still works from one that was rotated years ago.
Look in the places people forget while you are there. Test fixtures with real credentials in them, notebooks with output cells still populated, a docker-compose.yml written for local development, a .env.example that somebody filled in with real values to make onboarding easier, and the CI configuration file where a token was pasted "temporarily".
Should I rewrite history?
If the repository is public, yes, once the credential is rotated. It stops the secret being trivially discoverable by the next person who clones it, and it means an automated scanner does not keep raising it.
git filter-repo is the current tool; BFG still works and filter-branch is deprecated and slow enough to be a mistake. Both rewrite every commit hash after the change, which means every open pull request and every existing clone diverges, so agree the timing with anyone else who works on the repository. On GitHub you also need to ask support to garbage collect, because the old objects stay reachable through the API until they do.
If the repository is private and the credential is rotated, rewriting is optional. Weigh the disruption honestly rather than doing it because it feels thorough.
How do I stop it happening again?
Three controls, and the first is worth more than the other two combined. All three are set up once and then forgotten, which is the only kind that survives a busy month.
A pre-commit hook that blocks the commit. Gitleaks ships one. The point is stopping the commit from existing, since everything after that is cleanup, and a hook that runs in two seconds is a hook people leave installed.
Push protection on the host. GitHub offers it free for public repositories and it catches what a local hook misses, including commits pushed from a machine where nobody ran the setup.
A rule your coding agent inherits. Put "never write a credential into a file, always read from the environment, never commit .env" into your AGENTS.md, .cursor/rules or equivalent. Models follow project instructions reasonably well, and the failure mode you are preventing is the agent helpfully inlining a value it found in your terminal scrollback.
The part a scanner cannot tell you
A history scan tells you that a string matching a key pattern exists in commit a3f9c1. It does not tell you what that key can do, whether the account it belongs to is still active, or whether the same value is also sitting in a Slack message, a screenshot in a ticket, or a teammate's shell history.
That last part is judgement and it is yours. What can be automated is the reading, and this is one of the things Sentrint checks while it goes through your repository: secrets across history rather than just the current files, plus what an attacker would reach first in your code, in plain English, with a fix you can paste back into the tool that wrote it. The first scan is free and needs no card.
If you take one thing from the numbers at the top, take the 64%. The credentials that hurt people are not the ones nobody found; they are the ones somebody found, deleted from a file, and never rotated.