I leaked my API key, what do I do? (pushed AWS key to GitHub)
You leaked an API key: you pushed an AWS key to GitHub, pasted a token into a chat, or it showed up in a build log. This is common and it is recoverable. Work through the steps in order. The first one is the only urgent one.
1. Revoke or rotate the key at the provider
Do this before you delete the commit or the message. It is widely reported that bots scrape public GitHub pushes for credentials within minutes, so assume the key was copied the moment it became public. Deleting the evidence does nothing to a copy someone already has. Revoking the key does.
Create the replacement, deploy it, then kill the old one. If the key is public and has real permissions, kill it first and accept the outage.
- AWS IAM access keys: IAM console, the user's Security credentials tab, Access keys. Create a new key, make the leaked one inactive, confirm nothing broke, then delete it. From the CLI:
aws iam update-access-key --user-name NAME --access-key-id AKIA... --status Inactive aws iam delete-access-key --user-name NAME --access-key-id AKIA... - GitHub personal access tokens: Settings > Developer settings > Personal access tokens. Delete the token or regenerate it.
- Stripe: roll the key on the API keys page of the Dashboard, and set the old key to expire now instead of later.
- OpenAI and Anthropic: delete the key on the API keys page of the console and create a new one. Neither shows a key again after creation, so there is nothing to edit, only delete.
- Slack: open the app's settings at api.slack.com/apps and regenerate the secret or revoke the token. For an incoming webhook, remove it and add a new one.
For anything else, look for "API keys", "tokens" or "credentials" in the provider's account settings. Database passwords, signing secrets and webhook secrets all count.
2. Check whether the key was used
Look at the provider's audit log for the window between the leak and the revocation.
- AWS: CloudTrail event history, filtered by the access key ID. Look for new IAM users, new access keys, and instances launched in regions you do not use. Then check Billing. The IAM console also shows when and where each key was last used.
- GitHub: your account's security log, and the organization audit log if the token had organization access.
- Stripe, OpenAI, Anthropic, Slack: the request logs, usage pages and access logs in each dashboard.
If you find activity you cannot explain, contact the provider's support and tell your team. Anything an attacker created with the key (users, keys, tokens) survives the key's deletion and has to be removed separately.
3. Clean up the places the key still lives
The key is dead now, so this is tidying, not firefighting. It still matters: scanners keep flagging dead keys, and people copy old values back into use.
- Git history: amend the commit if it was never pushed, otherwise rewrite with
git filter-repo. Full steps in how to remove an API key from git history. - Shell history: if you typed
export AWS_SECRET_ACCESS_KEY=..., it is in plain text in your history file. See the guides for .zsh_history and .bash_history. - Chat messages: delete the message in Slack or Teams. It may persist in exports, retention archives and notification emails.
- CI logs: delete the affected job logs and move the value into the CI system's masked secret store.
- Screenshots and bug reports: check issue trackers, docs and screen recordings from the same session.
4. Make the next leak harder
- GitHub secret scanning and push protection: enable both under the repository's security settings. Push protection rejects a push that contains a recognized secret format.
- A pre-commit scanner such as gitleaks. Scan a repository's history from inside it:
Current releases also name this commandbrew install gitleaks gitleaks detectgitleaks git; rungitleaks --helpto see which your version lists. - .gitignore for .env: set it once, globally. See how to prevent committing .env files to git.
- A secrets manager: AWS Secrets Manager, 1Password, Doppler or Vault, so the value is fetched at runtime and never sits in a file next to your code.