How to Remove API Keys and Secrets From Git History

Removing a secret from the latest file does not erase it from earlier Git commits. If an API key, password, or token was committed, revoke or rotate it immediately; then assess where it was shared and, when needed, rewrite the repository history. This guide covers a careful cleanup using git-filter-repo and the follow-up steps that keep the old secret from resurfacing.

Practice Git workflows →

First: revoke or rotate the exposed credential

Treat a committed credential as compromised, even if the repository is private or the commit was later removed. Revoke the key or token in the service that issued it, create a replacement if the application still needs one, and update the application's approved secret store. Review the provider's audit logs and notify your security or platform team if your organization requires it.

Do this before rewriting history. History cleanup reduces future exposure, but it cannot make a credential safe again or prove that nobody copied it.

Identify the scope of the secret

Find which files and commits contain the exposed value, and check whether it reached a remote, a fork, a pull request, a release, a build log, or another system. Avoid copying a real credential into chat, tickets, terminal commands, shell history, or a new repository file while investigating. Use your provider's secret-scanning alerts or other approved tools when available.

If the file itself should be removed from every commit, record its repository-relative path. If the file must remain but a particular value should be redacted, plan a replacement rule instead. Make a verified backup before rewriting any shared history.

For a commit that exists only locally and has never been shared, you may be able to amend or reset it without a coordinated remote rewrite. If there is any possibility that others or systems already received the commit, follow your team's shared-history cleanup process.

Remove the secret with git-filter-repo

The recommended general-purpose tool for rewriting Git history is git-filter-repo. Install it using its official documentation, then work in a fresh mirror clone so you have the full set of remote refs and a separate working copy:

git clone --mirror https://github.com/OWNER/REPOSITORY.git repository-cleanup.git
cd repository-cleanup.git

Replace the example URL with your repository's URL. To remove an entire file from all rewritten history, use its exact path relative to the repository root:

git filter-repo --sensitive-data-removal --path config/secrets.env --invert-paths

If the file should remain but the exposed value needs to be replaced everywhere, create a private replacements file outside the repository. Its format can be:

literal:EXAMPLE_SECRET==>***REMOVED***

In that example, replace EXAMPLE_SECRET locally with the exact exposed value without putting the real value in a command or committing the replacements file. Keep the file access-restricted, do not share it, and remove it securely after cleanup. Then run:

git filter-repo --sensitive-data-removal --replace-text ../replacements.txt

Use the mode appropriate to your case, and check the tool's current documentation for version-specific flags and behavior. The tool may remove the origin remote as a safety measure; inspect the result locally, then add the correct remote back only when you are ready to publish the rewritten history.

Rewriting changes commit IDs and may also affect tags, branches, and other refs. Verify the cleaned history and the current application files before pushing. If the repository is hosted on GitHub, follow GitHub's sensitive-data removal guidance for repository-specific cleanup, including cached views and pull-request references.

Coordinate the rewritten history with your team

Replacing remote history is disruptive. Schedule the change, notify collaborators, pause pushes, and agree on a recovery plan before publishing. Once the approved rewrite is ready, restore the remote URL if needed, then push the rewritten branches and tags using the procedure agreed with your team and host:

git remote add origin https://github.com/OWNER/REPOSITORY.git
git push --force --all origin
git push --force --tags origin

These force pushes replace shared branch and tag history; do not run them without repository-owner approval and a coordinated plan. Protected branches may require a temporary, approved change to their protection settings. Confirm the remote state afterward.

Collaborators should follow the team's recovery instructions, often by making a fresh clone. Avoid merging or pushing old local branches that contain the removed commits, since they can reintroduce the secret. Coordinate cleanup of forks and any connected mirrors, CI logs, artifacts, backups, or caches as applicable.

Prevent secrets from entering Git again

Git history secret-removal FAQ

Is deleting the secret in a new commit enough?

No. Earlier commits still contain the original value. Revoke the credential, then decide whether a history rewrite is appropriate for the repository and its collaborators.

Does rewriting Git history revoke an API key?

No. Only the service that issued the credential can revoke or rotate it. History cleanup is a separate step to reduce future exposure.

Can the secret still be found after a force push?

Potentially. Other clones, forks, caches, pull-request references, backups, and logs may retain old data. Work with your host and collaborators to address those copies; never assume that rewriting a branch erased every copy.

Learn Git Faster by Practicing

Reading is just the first step. Real learning happens when you practice.

Start Learning Git →