Keys go in headers, never in URLs
git log -p -S "?key=" --all | grep -n "key= " | headgrep -rnE "( api_?key| token)= [A-Za-z0-9_-]{16, }" logs/ *. db 2>/ dev/ null | head
Get every Fix AI Slop Code episode
The code it wrote, the code it should have written and a check, for every episode
What is going on
A URL is not a private channel. It is written to the server log, the CDN log, the browser history, the error tracker's breadcrumbs, and anything that stores "the request" for debugging. A header is not. The provider's own header exists for exactly this reason.
Where it bit
The lead-gen auditor called Gemini with the key as ?key= because that is the shortest way to make the call work, and the model wrote it that way. Failed calls logged the full URL into the local database. 54 rows held the live key. The same key was shared with a second tool, so scrubbing the rows did not un-leak anything. Rotated same day.
The practice
Key in the x-goog-api-key or Authorization: Bearer header. A scrub_secrets() pass on anything that gets logged. A regression test that fails if a key pattern ever appears in a URL again. Tests went from 27 to 44.
Get this check as a script you can run tonight

The coding agent never holds production credentials
An agent with a production database URL will sooner or later run a migration or a cleanup against it, so production secrets never enter its env
Backups the app cannot reach, and one restore you have actually done
A backup on the same account the app or an agent can delete from is not a backup, and a restore you have never run is a number you do not have
2 min readClaude's /security-review found 31 vulnerabilities, and 5 were real
A product with four years of security work behind it ran Claude's /security-review. It reported 31 vulnerabilities. After the engineers checked each one, 5 were worth fixing
If this check came back with more than you expected, that is worth a conversation