I Accidentally Committed an API Key to Git: Now What?
It happens to nearly every developer at least once. You are moving fast, testing a feature locally, and before you realize it, you have pushed a commit containing a plaintext API key to a public GitHub repository. Within seconds, automated scanners have found it. Within minutes, someone may already be using it. Here is exactly what to do, step by step, and how to make sure it never happens again.
Step 1: Revoke the Key Immediately
This is the single most important action, and it must happen before anything else. Do not waste time trying to remove the commit first. Every second the key remains active is a window for abuse.
Go to the API provider's dashboard and either revoke, rotate, or regenerate the key. Most major providers have one-click revocation:
- AWS: IAM Console → Users → Security Credentials → Make Access Key Inactive
- Stripe: Dashboard → Developers → API Keys → Roll Key
- Google Cloud: Console → APIs & Services → Credentials → Delete and Recreate
- GitHub: Settings → Developer Settings → Personal Access Tokens → Revoke
- Twilio: Console → Account → API Keys → Delete Key
Generate a new key and store it in your secrets manager (not in code). Update your application to use the new key. Only after the old key is fully revoked should you move to the next step.
Step 2: Assess the Damage
Before cleaning your git history, figure out how much exposure occurred:
- Was the repo public? If yes, assume the key has been scraped. Bots scan GitHub continuously and can extract a key within seconds of it being pushed.
- How long was it exposed? Check your git log for when the commit was pushed. Check the API provider's usage logs for any unauthorized activity during that window.
- What permissions did the key have? A read-only analytics key is very different from a full admin key with billing access. Scope determines urgency.
- Was it a production key? If a production key with write access was exposed on a public repo, treat this as a security incident and notify your team immediately.
Check the API provider's audit logs for any requests made with the compromised key that you did not originate. For AWS, check CloudTrail. For Stripe, check the event log. For Google Cloud, check the Activity Log.
Step 3: Remove the Key from Git History
Simply making a new commit that deletes the file or replaces the key does not help. The old key is still visible in the commit history. You need to rewrite history to fully remove it.
Option A: BFG Repo-Cleaner (Recommended)
BFG is faster and simpler than git filter-branch. It is specifically designed for removing unwanted data from git repositories.
# Install BFG (requires Java)
brew install bfg
# Create a file listing the strings to remove
echo "sk_live_abc123yoursecretkey" > passwords.txt
# Clone a bare copy of the repo
git clone --mirror git@github.com:you/your-repo.git
# Run BFG to replace the secret with ***REMOVED***
bfg --replace-text passwords.txt your-repo.git
# Clean up and push
cd your-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force
Option B: git filter-repo
If you prefer staying within the git ecosystem, git filter-repo (the modern replacement for git filter-branch) works well:
# Install git-filter-repo
pip install git-filter-repo
# Replace the secret in all files across all history
git filter-repo --replace-text expressions.txt
# expressions.txt format:
# literal:sk_live_abc123yoursecretkey==>***REMOVED***
Both BFG and git filter-repo rewrite commit hashes. Every collaborator must re-clone the repository after the force push. Coordinate with your team before running these commands.
Step 4: Check for Cached Copies
Even after rewriting git history, copies of your key may still exist in:
- GitHub's cached views: Contact GitHub Support to purge cached copies of the commit. GitHub also runs secret scanning and may have already flagged the key.
- Forks: If anyone forked your repo before you cleaned the history, their fork still contains the old key. You cannot force-clean their fork.
- CI/CD logs: If the key was printed in build logs (Jenkins, GitHub Actions, CircleCI), those logs also need to be deleted.
- Web archives: The Wayback Machine and Google's cache may have snapshots. Request removal if the key appeared on a rendered page.
- Slack, email, or chat: If you shared the key in any messaging tool, those copies persist independently of git.
Step 5: Prevent It from Happening Again
Install pre-commit hooks
The most effective prevention is catching secrets before they enter git. These tools scan staged changes and block the commit if secrets are detected:
# Option 1: git-secrets (AWS-focused but extensible)
brew install git-secrets
cd your-repo
git secrets --install
git secrets --register-aws
# Option 2: detect-secrets (Yelp's tool, broader coverage)
pip install detect-secrets
detect-secrets scan > .secrets.baseline
detect-secrets-hook --baseline .secrets.baseline
# Option 3: gitleaks (fast, comprehensive)
brew install gitleaks
# Add to .pre-commit-config.yaml:
# - repo: https://github.com/gitleaks/gitleaks
# rev: v8.18.0
# hooks:
# - id: gitleaks
Use environment variables properly
# Bad: hardcoded in source
API_KEY = "sk_live_abc123"
# Good: loaded from environment
import os
API_KEY = os.environ["STRIPE_API_KEY"]
# Better: loaded from a secrets manager
import boto3
client = boto3.client('secretsmanager')
secret = client.get_secret_value(SecretId='prod/stripe/api-key')
API_KEY = json.loads(secret['SecretString'])['api_key']
Configure .gitignore
# Add to .gitignore
.env
.env.local
.env.production
*.pem
*.key
config/secrets.yml
credentials.json
Enable GitHub's push protection
GitHub offers free secret scanning and push protection for public repositories. When enabled, GitHub blocks pushes that contain recognized secret patterns (API keys, tokens, private keys) before they reach the repository. Enable it in your repository settings under Security → Code security and analysis → Push protection.
How Fast Do Scanners Find Exposed Keys?
Research from security firms and independent studies shows that exposed credentials on public GitHub repositories are discovered incredibly quickly:
- Within 1 minute: Automated bots scanning the GitHub Events API can detect new commits containing key patterns almost instantly.
- Within 5 minutes: In a 2023 study, researchers pushed fake AWS keys to public repos. Unauthorized usage began within 4 minutes on average.
- Within 24 hours: GitHub's own secret scanning program notifies API providers, but by then, damage may already be done.
This is why revocation must be your first action, not cleaning the git history. The key must be considered burned the moment it is pushed.
What If It Was a Private Repository?
The risk is lower but not zero. Private repository keys can still leak through:
- A collaborator forking the repo and making their fork public
- CI/CD logs that are publicly accessible
- The repository being made public later (accidentally or intentionally)
- A data breach at the git hosting provider
Best practice: treat private repos with the same hygiene as public repos. Never commit secrets to any repository. Use a secrets manager or environment variables for all credentials.
Accidentally committing an API key is recoverable, but only if you act fast. The priority order is: (1) revoke the key immediately, (2) assess damage, (3) clean git history, (4) check cached copies, (5) install prevention tools. The entire process should take less than 30 minutes. The prevention setup takes 10 minutes and saves you from ever repeating the experience.
Recommended Resources
To deepen your understanding of credential security and prevention techniques, these resources are highly recommended:
- The Web Application Hacker's Handbook — the definitive guide to finding and exploiting security flaws, including credential exposure vulnerabilities.
- Real-World Cryptography — understand the cryptographic foundations behind key management and secure storage.