I Leaked an API Key in My Vibe-Coded App: What to Rotate, In What Order
You searched your deployed site’s source, or you got an email from GitHub, or your OpenAI bill jumped overnight — and there it is: a secret key sitting in code that anyone can read. This is the most time-sensitive problem a vibe-coded app can have, and the response is the same every time: rotate first, investigate second, fix the code third. Here is the order.
Rotate the key. Go to the provider’s dashboard, create a new key, and revoke the old one. Do not delete the file, do not rewrite the commit, do not “make it private” — none of that un-leaks a key that has already been read. Bots scan public repositories and deployed bundles continuously; a leaked key can be found and used within minutes of being pushed.
Which keys, in which order
If several things leaked at once, rotate in the order of what they can do:
service_role key, Firebase admin SDK credentials, database connection strings with passwords. These read and write every row regardless of your policies. Rotate the key and the database password.Then: find out what happened
Once the old keys are dead, look at usage. Every provider has a dashboard: OpenAI and Anthropic show requests and spend by key; Supabase shows API logs; AWS has CloudTrail; Stripe shows API request logs. You’re looking for activity you didn’t generate — unusual volume, odd hours, unfamiliar IP ranges. If you find any, note the time window: that’s the period during which your data may have been read, and it matters for the next step.
If the leaked key could read user data (category 2 above) and you see unexpected access, you may have a disclosure obligation depending on where your users are. That’s a question for a lawyer, not a blog post — but the answer is easier when you have the log window in hand.
Then: stop it happening again
Get the secret out of the code
Secrets belong in environment variables on the server, read at runtime. In a Next.js app, anything without the NEXT_PUBLIC_ prefix stays server-side; in Vite, anything without VITE_. AI tools frequently pick the public prefix because it makes the call “just work” from the browser — which is exactly the leak. Move the call behind a server route or edge function and keep the key there.
Scrub the git history (after rotating)
Deleting the file doesn’t remove the old commit. If the repo is or might become public, rewrite history with git filter-repo or BFG, force-push, and ask collaborators to re-clone. This is cleanup, not protection — the key is already rotated, so this just stops the dead key showing up in scans forever.
Add a tripwire
Enable GitHub secret scanning and push protection on the repo (free for public repos, and available on private ones). Add a pre-commit hook such as gitleaks. Set spend alerts and hard caps on every paid API. Put a rate limit on any endpoint that calls a paid service, so a leaked public endpoint can’t run up your bill either — see the vibe coding security checklist for the rest of that list.
How this fits the bigger picture
A leaked key is rarely the only gap. The same “make it work” pressure that put the key in the client usually also skipped ownership checks and server-side validation. Once the fire is out, the 12-point prototype audit is the systematic pass. And if you’re not confident you found everything — or you’re not sure which keys are which — that is a reasonable moment to bring in an engineer for a day rather than guess.
FAQ
Not sure you found everything?
One-day secrets and access review: we sweep the bundle, the repo history, the env config and the provider logs, rotate what needs rotating, and leave you with tripwires in place.
