Stop API Keys Leaking into Git in 12 Minutes

Prevent accidental API key commits with pre-commit hooks, .gitignore patterns, and git-secrets. Tested solutions for 2026.

Problem: You Just Pushed API Keys to GitHub

You committed .env with your OpenAI API key. GitHub immediately sent a security alert. Your key is now in git history forever, even if you delete the file.

You'll learn:

  • How to block secrets before they're committed
  • Automated scanning with pre-commit hooks
  • How to remove keys from git history
  • Prevention strategies that actually work

Time: 12 min | Level: Beginner


Why This Happens

Developers copy API keys into code for testing. IDEs autocomplete file paths. .env files get staged with git add . without thinking. By the time you notice, it's already pushed.

Common symptoms:

  • GitHub security alert emails
  • Revoked API keys from providers
  • Exposed credentials in public repos
  • Keys buried in commit history

Solution

Step 1: Install git-secrets

This AWS tool scans commits for common secret patterns before they're added to history.

# macOS
brew install git-secrets

# Linux
git clone https://github.com/awslabs/git-secrets
cd git-secrets
sudo make install

# Windows (Git Bash)
git clone https://github.com/awslabs/git-secrets
cd git-secrets
./install.ps1

Expected: Command completes without errors.


Step 2: Configure git-secrets for Your Repo

cd your-project

# Initialize git-secrets
git secrets --install

# Add patterns for common API keys
git secrets --register-aws  # AWS keys

# Custom patterns for other providers
git secrets --add '[A-Za-z0-9_]{32,}'  # Generic 32+ char tokens
git secrets --add 'sk-[A-Za-z0-9]{48}'  # OpenAI keys
git secrets --add 'ghp_[A-Za-z0-9]{36}'  # GitHub PATs
git secrets --add 'AIza[0-9A-Za-z_-]{35}'  # Google API keys

Why this works: Git-secrets runs on every commit, scanning staged files for patterns. If found, the commit is rejected before it enters history.

If it fails:

  • Error: "not a git repository": Run git init first
  • Permission denied: Add sudo to install command on Linux

Step 3: Set Up Pre-commit Hooks

Use the pre-commit framework for more comprehensive checks.

# Install pre-commit
pip install pre-commit --break-system-packages

# Create config file
cat > .pre-commit-config.yaml << 'EOF'
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: detect-private-key  # SSH keys, certificates
      - id: check-added-large-files
        args: ['--maxkb=1000']
      
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ['--baseline', '.secrets.baseline']
EOF

# Install hooks
pre-commit install

# Create baseline (scans existing files, creates allowlist)
detect-secrets scan > .secrets.baseline

Expected: Hooks install successfully. .secrets.baseline file created.


Step 4: Add Proper .gitignore Patterns

cat >> .gitignore << 'EOF'

# Environment files
.env
.env.*
!.env.example

# API keys and credentials
*.key
*.pem
*.p12
*.pfx
credentials.json
secrets.yaml
config/secrets/

# Common secret locations
.secret
.secrets/
private/
EOF

Why this works: Prevents accidental staging of known secret file patterns. The !.env.example line allows example files through.


Step 5: Test Your Protection

# Create a fake secret
echo "OPENAI_API_KEY=sk-1234567890abcdefghijklmnopqrstuvwxyz123456" > .env

# Try to commit it
git add .env
git commit -m "test: adding env file"

You should see: Commit rejected with error message showing the blocked secret pattern.

[ERROR] Detected secrets in .env
OPENAI_API_KEY=sk-************************************
Commit rejected.

If You Already Committed Secrets

Remove from History (Nuclear Option)

# WARNING: This rewrites git history
# Coordinate with your team first

# Install BFG Repo Cleaner
brew install bfg  # macOS
# Or download from: https://rtyley.github.io/bfg-repo-cleaner/

# Remove secrets
bfg --replace-text secrets.txt  # File with one secret per line
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# Force push (only if you control the repo)
git push --force

Alternative (Surgical Approach):

# Remove specific file from history
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch .env" \
  --prune-empty --tag-name-filter cat -- --all

Critical: After removing secrets from history:

  1. Revoke the exposed keys at provider dashboard
  2. Generate new keys
  3. Update deployment configs
  4. Notify team members to re-clone the repo

Verification

# Test that hooks work
echo "test_key=sk-abcd1234" > test.txt
git add test.txt
git commit -m "test"

# Should be rejected

You should see: Commit blocked with secret detection message.

# Clean up test
git restore --staged test.txt
rm test.txt

What You Learned

  • git-secrets blocks commits with API key patterns
  • pre-commit hooks add multiple layers of protection
  • .gitignore prevents accidental staging
  • History rewriting is last resort, always revoke keys first

Limitations:

  • Pattern matching isn't perfect (false positives/negatives)
  • Doesn't scan files already in history
  • Won't catch obfuscated secrets

When NOT to use this:

  • Secrets already in history (revoke first, then clean)
  • Shared repos where you can't rewrite history (coordinate cleanup)

Production Checklist

  • git-secrets installed on all team machines
  • Pre-commit hooks in project README
  • .env.example file with fake values as template
  • CI/CD secret scanning enabled
  • Key rotation schedule documented
  • Security policy in SECURITY.md

Advanced: Organization-Wide Setup

For teams, enforce this globally:

# Enable for ALL repos
git secrets --install ~/.git-templates/git-secrets
git config --global init.templateDir ~/.git-templates/git-secrets

# Every new repo now has git-secrets enabled

Add to team onboarding:

# .bashrc or .zshrc
alias git-init='git init && git secrets --install && pre-commit install'

Tested on Git 2.43+, git-secrets 1.3.0, pre-commit 3.6.0 Platforms: macOS 14, Ubuntu 24.04, Windows 11 (Git Bash)

Found this helpful? Share with your team before the next security incident.