Quick Answer
A pre-commit hook with Gitleaks scans your staged files for API keys, passwords, and tokens before they reach Git history. Once a secret is committed, removing it from history is painful and error-prone. Prevention is 100x easier than cleanup. Setup takes under 5 minutes with either Husky (npm) or the pre-commit framework (Python).
Why Prevention Beats Cleanup
Removing a secret from your current code does not remove it from Git history. Every commit is permanent. Anyone who clones your repo - or who cloned it before you noticed - has access to every secret you ever committed. GitGuardian's 2024 report found 12.8 million new secrets exposed in public repositories in 2023, a 28% increase from the previous year.
Cleaning secrets from Git history requires git filter-branch or BFG Repo-Cleaner, both of which rewrite history and break every fork and clone. It also requires rotating the exposed credential, which means generating new keys, updating every environment that uses them, and hoping nobody grabbed the old one.
A pre-commit hook prevents all of this by rejecting the commit before the secret ever enters history. According to GitHub's 2024 Octoverse report, repositories with secret scanning enabled caught 3x more secret exposures before they became incidents compared to repos without scanning.
Option 1: Gitleaks with Husky (npm Projects)
For JavaScript/TypeScript projects, Husky is the standard way to manage Git hooks. Here is how to add Gitleaks as a pre-commit hook:
// Step 1: Install Husky
npm install --save-dev husky
npx husky init
// Step 2: Install Gitleaks (macOS/Linux)
// brew install gitleaks
// Or download from: https://github.com/gitleaks/gitleaks/releases
// Step 3: Create the pre-commit hook
// .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
echo "🔍 Scanning for secrets..."
gitleaks protect --staged --verbose
if [ $? -ne 0 ]; then
echo "❌ Secrets detected! Commit blocked."
echo "Remove the secrets and try again."
exit 1
fi
echo "✅ No secrets found."
Option 2: Pre-commit Framework (Python)
The pre-commit framework works with any project type and manages hook dependencies automatically:
// .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
// Install and activate:
// pip install pre-commit
// pre-commit install
What Gitleaks Detects
Gitleaks uses 150+ built-in rules to detect secrets. It catches patterns that are common in vibe-coded projects:
| Secret Type | Pattern Example | Common Source |
|---|---|---|
| AWS Access Keys | AKIA... |
AI-generated S3 upload code |
| Stripe Secret Keys | sk_live_... |
Payment integration code |
| GitHub Tokens | ghp_... |
CI/CD scripts, API integrations |
| Supabase Service Keys | eyJhbGciOi... (JWT) |
Database client initialization |
| OpenAI API Keys | sk-... |
AI feature integration |
| Private Keys (RSA/SSH) | -----BEGIN RSA PRIVATE KEY----- |
Deployment configs, auth setups |
| Database Connection Strings | postgresql://user:pass@... |
ORM configuration files |
Handling False Positives
Sometimes Gitleaks flags test fixtures, documentation examples, or intentionally public keys (like Supabase anon keys). Create a .gitleaksignore file to suppress known false positives:
// .gitleaksignore - one fingerprint per line
// Get the fingerprint from the Gitleaks output
# Test fixtures with fake API keys
3a5b7c9d1e2f4a6b8c0d2e4f6a8b0c2d
# Supabase anon key (safe to expose)
9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c
Best Practices for Teams
Pre-commit hooks run locally, which means every developer on your team needs them installed. Here is how to make sure nobody skips the step:
- Add Husky to devDependencies - it auto-installs hooks on
npm install - Add a CI/CD scan too - pre-commit hooks can be bypassed with
--no-verify, so run Gitleaks in CI as a safety net - Document in README - explain why the hook exists and how to handle false positives
- Run a baseline scan first - before adding the hook, scan your existing history with
gitleaks detectto find and rotate any already-committed secrets
Tools like VibeDoctor (vibedoctor.io) scan your entire codebase for committed secrets, hardcoded credentials, and exposed API keys, catching what pre-commit hooks might have missed before they were installed. Free to sign up.
FAQ
Can pre-commit hooks be bypassed?
Yes. Running git commit --no-verify skips all hooks. This is why you should also run Gitleaks in your CI/CD pipeline as a second layer. Pre-commit hooks catch 95% of cases during normal development. CI catches the rest.
Do pre-commit hooks slow down commits?
Gitleaks with --staged only scans the files you are committing, not the entire repo. For most commits, this takes 1-3 seconds. If you are committing hundreds of files at once, it may take up to 10 seconds. The delay is negligible compared to the cost of a leaked secret.
What if I already committed secrets before adding the hook?
Run gitleaks detect on your full repository to find existing secrets. Rotate every exposed credential immediately (generate new keys, update all environments). Then use BFG Repo-Cleaner to remove the secrets from Git history. Finally, add the pre-commit hook to prevent future leaks.
Is Gitleaks better than GitHub secret scanning?
They are complementary. GitHub secret scanning runs server-side after you push. Gitleaks with pre-commit hooks runs client-side before you commit. GitHub scanning notifies you when a secret reaches the remote - but by then it is already in history. Pre-commit hooks prevent the commit entirely. Use both.
Does this work with Cursor, Bolt, or other AI tools?
Yes. Pre-commit hooks are Git-level, not editor-level. They fire regardless of which tool generated the code. This is especially valuable with AI tools because they frequently hardcode keys and tokens that a human developer would instinctively put in environment variables. The hook catches what the AI gets wrong.