You pushed a secret. What now?

An API key, a token or a password is in a commit, a build log or a public paste, and it should not be. Here is the order to do things in — and why deleting the commit is neither step one nor step two.

Rotate first, clean up second. The only action that ends the exposure is invalidating the credential where it was issued. Rewriting history, deleting the commit or flipping the repository to private all change how conveniently someone can find the secret. None of them change whether it still works.

Assume it was already collected. Public pushes are watched continuously by people who are not you. Plan as though a stranger has had the key since the moment it was pushed, because the cost of being wrong in that direction is a few minutes of rotation, and the cost of being wrong in the other direction is your bill.

What rewriting history does not reach
You push a commit containing a live credential
The commit object itself Still fetchable by its hash after a force-push, until the host chooses to collect it. Nothing obliges it to, and the hash is in your reflog and everyone else’s.
Every fork Forks share one object store with the original. A single fork keeps the object reachable no matter what you do to your branch.
The broadcast Webhooks, mirrors, event firehoses and credential scanners were told about the push as it happened. Some of them are looking for exactly this.
The copies CI logs, build caches, container layers, clones on laptops, notification emails. Each is a separate deletion you would have to find and win.

Revoking the credential cuts every one of those at once, because it makes the copies worthless instead of hunting them. Rewriting history cuts none of them. That is the entire argument for the order below.

The order

  1. Revoke or rotate it at the issuer. Not in your code — in the console or API that minted it. Until that is done, nothing else you do matters. If the credential is load-bearing in production, create the replacement first, deploy it, then kill the old one; if it is not, kill it now and fix the breakage after.
  2. Check whether it was used. Every serious issuer keeps a log of what a credential did. This is the step that turns a scare into a known quantity, and it is the one most people skip because rotation feels like the end of the story. It is not: rotation stops the future, and only the audit log tells you about the past.
  3. Then clean the history — knowing it is hygiene, not remediation. It stops the next person who clones the repo from picking the secret up by accident, which is worth doing. It does not retrieve anything.
  4. Look for the rest. A secret in a commit is rarely alone. The habit that put it there — a .env that was never ignored, a config committed “just to get CI green”, a debug print of the whole environment — usually put others somewhere too. Scan the whole history, not just the tip.
  5. Write down what happened while it is fresh. Which credential, what it could reach, when it was exposed, when it was revoked, and what the audit log said. If anyone ever asks — a customer, an auditor, your future self — that note is the difference between an incident you handled and one you cannot describe.

Why deleting the commit does not do what you think

All four of these are ordinary, documented behaviour, not exotic attacks.

The commit is still reachable by its hash

Force-pushing moves the branch. It does not immediately destroy the object. On a hosted service the old commit typically stays fetchable by its full SHA until the server garbage collects, and anyone who saw the push already has the SHA. “It is gone from the branch” and “it is gone” are different sentences.

Forks share the object store

On a hosted fork network, objects live in a shared pool. A commit removed from the repository you control can remain reachable through a fork you do not control — including one created after you noticed. You cannot force-push someone else's fork.

The push itself was broadcast

Public activity feeds publish push events, and there are public archives of that firehose. Automated collectors read it in real time looking for exactly this. The window between pushing a secret and someone else holding it is not measured in hours.

Copies escape the repository

If the secret shipped in anything — a container image layer, a published package tarball, a client-side bundle, a CI log, a cached page — deleting the source commit does not touch those copies. Each one is a separate cleanup with its own deadline.

Where to revoke it, by what it looks like

If you are not sure what you are holding, the field guide to credential prefixes identifies it from the first few characters. Then:

CredentialWhere it dies, and what to watch
AWS access key
AKIA…
IAM → the user → security credentials: deactivate the key, then delete it. Read CloudTrail filtered on that access key id. Unfamiliar regions are the classic tell — compute spun up somewhere your team has never deployed. Check for new IAM users or keys created after the exposure; that is how a short visit becomes a long one.
GitHub token
ghp_…, github_pat_…
Developer settings → personal access tokens → delete. The token page shows a last-used date, which is a fast first answer. Organisation owners also have an audit log. Note that some providers partner with code hosts to auto-revoke keys detected in public repositories, so yours may already be dead — verify that, do not assume it.
Payment or billing API key
sk_live_…
Roll it in the provider dashboard rather than deleting it outright if there is a grace-period option, so live traffic does not fail while you deploy. Then read the API request log for calls you cannot account for.
Model-provider key
sk-…, sk-ant-…
Revoke on the API keys page. These are stolen for resale and the abuse shows up as spend, so the usage page is your audit log. Set a spending limit while you are there.
Google service account
.json key file
IAM & admin → service accounts → keys → delete the key id. Then look at Cloud Audit Logs for that principal. Also ask what the account could reach: a service account is often over-granted, and the blast radius is the point.
Slack / chat bot token
xox…
Revoke in the app's OAuth settings and reinstall the app. Bot tokens read history, so treat the channels it could see as exposed too.
Private key
-----BEGIN … PRIVATE KEY-----
There is no issuer to call. Generate a new pair and replace it everywhere the public half is trusted — authorized_keys, certificates, deploy keys. If it signed tokens, every token it ever signed must now be treated as forgeable: publish a new key id and stop accepting the old one. This is the most expensive case on this page.
Database URL with a password Change the password, then check the server's connection log for sources you do not recognise. If the database was reachable from the public internet, that is the finding, and it outlives this one key.

It was not a repository

The order is the same — rotate, check, clean — but “clean” means different things and some of them are not available to you at all.

Where it leakedWhat cleanup actually means
CI build logDelete the run, and mark the variable as secret so it is masked next time. Masking only works if the runner knows the value, which is why an echoed derived value still prints in the clear.
Chat, issue tracker or AI assistantEditing removes the current version; the edit history, notification emails and any retention on the provider's side are not yours to delete. Rotate and move on.
Pastebin or gistDeletion works if you get there first, and search engine and archive caches are the reason you often do not.
Published package or imagePublish a fixed version, then handle the old one by the registry's rules. Yanking hides it from resolution; it does not always remove the bytes, and anyone who already installed it has them.
Client-side bundle or mobile appIt was public the moment it shipped. Anything embedded in a client is not a secret — the fix is architectural: move the call behind something you control.
Screenshot or screen recordingPeople blur keys and forget the terminal title bar, the browser tab and the notification that appeared mid-recording. Re-watch it before you decide only one thing leaked.

Stopping the next one

Two of these are free tools on this site; the third is a habit that costs nothing.

Scrub the log before it leaves your machine Paste a log, a stack trace or a config dump and get it back with the credentials replaced by numbered placeholders, so it is still readable enough to debug from. It runs entirely in your browser — nothing is uploaded. If you issue the tokens, give them a prefix A distinctive, greppable prefix and a checksum are what let scanners — yours and everyone else's — find your token in a haystack and tell it apart from a hash. It is the single cheapest thing a token format can do for the people who leak it.

And the habit: decide that logs, screenshots and pastes are public by default, and check them on the way out rather than after. The two-minute version of that check is here.

What this page does not cover

This is about a credential leaking as text that you can point at. It is not about a compromised machine, a malicious dependency or a breach at the provider — in those cases you do not know what the attacker holds, and the response is a different and larger one.

It also stops at the technical response. If the credential could reach personal data or customer data, whether you are obliged to tell anyone is a question for your organisation and its lawyers, not one I will answer. I have written the engineering half here because the engineering half is where the clock is running.