Draft, not yet published
How Claude Code projects leak API keys, and how to catch it before launch
7 July 2026· 5 min read · by Stackbastion
You built a project with Claude Code, it works, and you’re about to push it live or share the repo. Before you do, there’s a good chance an API key is sitting in the code where it shouldn’t be. Not because the tool is careless, but because of how generated projects come together. Here’s exactly how keys leak, and a few commands to find them before anyone else does.
Why this happens
AI coding tools write working code fast. To make an example run end to end, it’s often easiest to drop a real value inline: a database URL, a Stripe key, an OpenAI token, right there in the source. The code works, you move on, and the key stays.
There are four common ways a key ends up exposed:
Hardcoded in source. A key written directly into a .ts, .py, or config file instead of read from an environment variable. It works locally and it works in production, which is exactly why nobody notices it’s a problem.
Committed .env files. The secrets live in a .env file (good), but the file gets committed to git because .gitignore didn’t cover it (bad). Now every key is in your git history, and history is forever unless you rewrite it.
Keys baked into the frontend. A secret used in client-side code gets bundled into what ships to the browser. Anyone can open dev tools and read it. This is common when a key that should live on the server gets used in a React component.
Keys in the git history even after you “removed” them. You deleted the key from the current file and committed the fix, but the old commit still contains it. Deleting a secret in a new commit does not remove it from history.
The dangerous part is that a leaked key is silent. Nothing breaks. The app runs fine. You find out when someone runs up a bill on your OpenAI account or drains a Stripe balance, and by then the key’s been public for weeks.
How to fix it
Give yourself ten minutes and run these checks before launch.
Step 1: grep the working tree for anything that looks like a key.
Most keys have recognizable prefixes. Search for them directly:
grep -rniE "(sk-[a-z0-9]{20,}|sk_live_|pk_live_|AKIA[0-9A-Z]{16}|ghp_[a-z0-9]{36}|xox[baprs]-)" \
--include="*.{ts,tsx,js,jsx,py,json,env,yml,yaml}" .
That pattern catches OpenAI keys, Stripe live keys, AWS access keys, GitHub tokens, and Slack tokens. If anything comes back, you have a hardcoded secret to move into an environment variable.
Step 2: check whether a .env file got committed.
git ls-files | grep -E "\.env"
If this prints anything other than .env.example, you’ve committed secrets. Stop and treat those keys as compromised.
Step 3: search the entire git history, not just the current files.
This is the step people skip. A key you deleted last week is still in the history:
git log -p --all -S "sk-" -- . | grep -iE "sk-|secret|api_key|password" | head
If a real key shows up in an old commit, deleting it now isn’t enough. You need to rotate it (see step 5) and, if the repo is public, treat it as already leaked.
Step 4: fix the .gitignore so it can’t happen again.
# .gitignore
.env
.env.local
.env.*.local
*.pem
Keep a committed .env.example with empty values so the next person knows what to fill in, and read every secret from the environment in code:
const stripeKey = process.env.STRIPE_SECRET_KEY;
if (!stripeKey) throw new Error("STRIPE_SECRET_KEY is not set");
Fail loud when a secret is missing. A clear crash at startup beats a silent misconfiguration in production.
Step 5: rotate anything that was ever exposed.
If a key touched a commit, a public repo, or a client bundle, assume it’s compromised and rotate it. Revoke the old one in the provider’s dashboard and issue a new one. Moving it into an env var doesn’t un-leak the old value.
For the frontend leak specifically: any secret that ended up in browser code has to move server-side. The browser can never hold a real secret. Route the call through your own backend instead.
We covered the fast version of this in the exposed keys 5-minute check, which is worth bookmarking for every project.
Or, we do it for you
We run this scan as part of every production audit, across the working tree, the git history, and the shipped bundle, then tell you exactly what to rotate. If you want a second pair of eyes on your setup regardless of what you built it with, get a free production audit.
FAQ
Claude Code hardcoded a key in my project. Is that a bug?
Not really. Generating runnable example code sometimes means inlining a value so the example actually works. The fix is on you: move it to an environment variable and, if it was ever committed or shared, rotate it. Treat every generated project as something to scan before launch.
I deleted the key from my code. Am I safe now?
Only if it was never committed. If it’s in your git history, deleting it in a new commit leaves the old value fully readable in the old commit. You have to rotate the key, and for a public repo, assume it’s already been scraped.
How do bots find leaked keys so fast?
Automated scanners watch public repos and new commits continuously and grab anything matching known key patterns within minutes. That’s why “it’s only been up for an hour” isn’t reassuring. Speed of rotation is what protects you.
What if a secret is in my frontend bundle?
Then it’s public, full stop. Anything shipped to the browser can be read by anyone. Rotate that key and move the call that used it to your server, where the secret can actually stay secret.