Catch API Keys Before They Hit GitHub
A leaked API key on a public GitHub repo is scraped and abused within minutes, and deleting it later doesn't help — it's in git history forever. Catch secrets in two layers: a local pre-commit hook that blocks the commit before it's created (true prevention), plus a scanner that checks every push and flags anything that slipped through, so you can rotate it before it's exploited.
There's a live market of bots doing exactly one thing: watching public GitHub events and grabbing freshly committed API keys — Stripe, AWS, OpenAI, Supabase. The window between "I pushed" and "someone is using my key" is measured in minutes, sometimes seconds. So the real goal isn't tidy code; it's making sure a secret never reaches a place it can be scraped from — and if one does, catching it fast enough to rotate before it's abused.
Here's the mistake, and why it's stubborn:
import Stripe from "stripe";
// Pasted in "just to test" — now one commit from being public.
const STRIPE_SECRET_KEY = "sk_live_51Nk2pLZvKq9rT4xBwHc8mYdQ";
export const stripe = new Stripe(STRIPE_SECRET_KEY);
This is what GuardLayer reports on that file — live engine output, with the fix inline:
- Criticallib/stripe.ts:4
Hardcoded secret in source
Move the secret to an environment variable, purge it from git history (e.g. git filter-repo / BFG), and rotate it at the provider.
The trouble is that deleting the line later doesn't remove the key — git keeps every past commit. So prevention has to happen early, and detection has to happen fast.
The honest picture: where can you actually stop a secret?
Be clear about what stops a leak at which stage, because tools get marketed loosely here:
- Pre-commit / pre-push (local git hook): the only place you can block a secret before it leaves your machine. This is true prevention — the commit or push is rejected.
- On push (a scanner watching the repo): runs after the code reaches GitHub and flags what got through, within seconds. It can't un-push a commit — no GitHub app can — but it tells you immediately so you rotate before the key is exploited, and it stops the secret from advancing further (into a PR merge, into
main, into a deploy).
You want both layers. The local hook catches the obvious cases for free; the push scan is the backstop for everything the hook missed or that was bypassed with --no-verify.
Layer 1: block the commit locally
Add a pre-commit hook that scans staged changes for secret patterns and refuses the commit. gitleaks is the common choice:
# .git/hooks/pre-commit (or wire it via the `pre-commit` framework)
#!/bin/sh
gitleaks protect --staged --redact --verbose || {
echo "🚫 Secret detected in staged changes — commit blocked."
exit 1
}
This is genuine prevention: the key never enters even a local commit. The catch is that hooks are per-machine and skippable (git commit --no-verify), so you can't rely on them alone across a team or your future rushed self.
Layer 2: scan every push as the backstop
This is where a repo-connected scanner earns its place. GuardLayer runs on every push, reads the diff against your whole codebase, and flags a hardcoded key (and a secret behind a NEXT_PUBLIC_ prefix, which a plain secret scanner often misses) as critical — posting a pass/fail check on the commit and PR. To be precise about what that does: it catches and reports the leak in seconds; it doesn't reject the raw push. If you want a failing check to actually block the code from merging into main, make it a required status check in your branch protection — otherwise it's an immediate alert, not a hard gate.
Either way, the value is speed: you learn a key shipped while there's still time to rotate it.
If a key already reached GitHub
Assume it's compromised the moment it's public. Then:
- Rotate immediately — revoke it at the provider and issue a new one. Do this first, before cleanup.
- Move the new key server-side — into an untracked
.env, read viaprocess.envin aserver-onlymodule (keeping secrets out of Next.js). - Purge history —
git filter-repoor BFG, then force-push. The service_role key and any provider key must be scrubbed from every past commit, not just the latest.
FAQ
Can anything actually stop me from pushing a secret to GitHub? Only a local git hook (pre-commit or pre-push) can block it before it leaves your machine. A repo-connected scanner runs after the push and catches it within seconds — fast enough to rotate — but it can't reject the push itself. Use both.
Isn't deleting the commit enough if I catch it fast? No. Even a force-push doesn't guarantee removal from forks, caches, or anything already scraped. Always rotate the key; treat cleanup as secondary.
Does GuardLayer replace gitleaks?
They're complementary. gitleaks blocks the commit locally; GuardLayer scans every push as a server-side backstop and also catches Next.js/Supabase-specific leaks (like a NEXT_PUBLIC_ secret) that generic secret scanners don't flag.
How fast are leaked keys really exploited? Public-repo secrets are routinely abused within minutes — automated scrapers watch GitHub's public event stream continuously. Speed of detection and rotation is the whole game.
Catch this before it ships — free
GuardLayer scans every push for this and 19 other Next.js + Supabase issues, with the exact fix inline.
No signup, no card — your code is scanned in memory and never stored.