If Claude Code wrote an API key into your source and committed it, rotate the key first, then clean up the history and move it to an environment variable. Claude Code writes strong code, but like every assistant it will inline a key when that is the shortest path to something that runs, so the guard has to live in your workflow.
Why it's a problem
A committed key is readable by anyone with the repo and is scraped from public GitHub within minutes. Claude Code also stamps commits with a co-author trailer, so a commit containing a key is clearly attributable and easy for a scraper to find alongside the rest of your history.
How to fix it, in order
- 1Rotate the key first. Revoke and regenerate it at the provider. This is the only step that protects you once the key has been committed.
- 2Check the provider's logs. Look for use you did not create and treat anything unexplained as an incident.
- 3Purge it from git history. Remove it with git filter-repo or BFG and force-push; forks and caches can still hold the old commit, which is why rotation came first.
- 4Move it to an environment variable. Read the new key from process.env or import.meta.env and confirm the .env is gitignored.
Why Claude does this
Claude Code optimizes for correct, working code, and when an integration needs a key the inline value is the completion that makes the code run immediately. It is not weighing the long-term exposure of a committed secret against the short-term win of a working call, because that is a human judgment about consequences.
Stop it happening again
Add a rule to CLAUDE.md telling Claude Code to always read secrets from environment variables and never inline them. Keep a gitignored .env with a committed .env.example so it learns the variable names, not the values. Install a gitleaks pre-commit hook as the gate that actually blocks a key that slips through.
The quick fix
- Rotate the key first.
- Check provider logs for unauthorized use.
- Purge from git history; move the key to an environment variable.
- Add a secrets rule to CLAUDE.md and install a gitleaks pre-commit hook.
Common questions
Does CLAUDE.md actually prevent Claude Code from writing secrets inline?
Yes, when you add an explicit rule. Claude Code reads CLAUDE.md as project-level instructions and follows rules like 'never inline secrets; always read from process.env.'
Claude Code uses my shell environment. Can it read and expose .env values?
Claude Code can read files it is given access to, including .env if it is in the working directory. Keep sensitive .env files outside the working directory or add a CLAUDE.md rule restricting file access.
What makes a gitleaks hook a better guard than reviewing output manually?
The hook runs automatically on every staged commit regardless of which tool wrote the code, so a secret never reaches git history no matter how it got into the code.