One in five AI-built apps is leaking a key. Check yours in 30 seconds
· 3 min read · by Nikhil Sebastian
A scan of about 130,000 vibe-coded sites found a secret key in one of every five. Here is the 30-second check, the real bills people paid, and the free fix prompt for Claude.
When you ask an AI to call OpenAI, Gemini or Supabase from your page, it often pastes your secret key straight into the browser code. Anyone who opens your site can read it. Then they spend your money. This is the written version of the reel, with the numbers, the check and the fix prompt so you can copy them.
The number
RedHunt Labs scanned about 130,000 published websites built on 13 vibe-coding platforms. Roughly 26,000 of them, one in five, exposed a working secret. That is about 25,000 unique keys, including 16,000+ Firebase and 3,000+ Supabase credentials. A separate January 2026 scan of 20,000 launch URLs found 11% exposing Supabase credentials in frontend code. The study is here.
Real people, real bills
- $40 overnight. A builder found their OpenAI key in view-source after letting the AI write the frontend. r/vibecoding
- $1,000 drained in one night. An image-generation app shipped with the key in the frontend. The prepaid balance was gone by morning. r/vibecoding
- $87,500 stolen. A founder vibe-coded a SaaS; attackers found the keys in the frontend. r/vibecoding
- About $6,700. An AI agent put a Gemini key into a public Vite bundle without warning. Post-mortem
- 121,000 credits in a day. A committed Claude Code settings file leaked a key. r/vibecoding
These are self-reported, so treat the exact figures as the posters gave them. The pattern is the same every time: a key that only the server should hold ended up in code the browser downloads.
The 30-second check
- Open your live site in a desktop browser.
- Right click, choose View Page Source.
- Press Cmd+F (Ctrl+F on Windows) and search for each of these:
sk-,AIza,eyJ,supabase,Bearer. - Repeat inside any
.jsfile the page loads (click its link in the source).
Anything you can see there, everyone can see. A Supabase anon key alone is fine only if Row Level Security is on for every table; if you never set RLS up, treat it as a leak.
The fix, three steps
- Rotate the key now. In the provider dashboard, revoke the exposed key and create a new one. The old one is dead the moment you click, which is the point.
- Move the call to a server route. Paste the prompt below into Claude Code (or Cursor, or Lovable). The browser should only ever talk to your own server; your server talks to OpenAI.
- Set a spending cap. Every provider has a monthly budget or hard limit setting. Set it to what you can afford to lose.
The fix prompt
Audit this project for secrets in frontend code (anything served to the browser).
Move every third-party API call (OpenAI, Gemini, Anthropic, Stripe, Supabase service role) to a server-side route or edge function.
Read keys only from environment variables; never hardcode them.
Add .env and .env.* to .gitignore and confirm no key is committed in git history.
Show me the diff before applying anything.
After it applies the change, run the 30-second check again. The search for sk- should return zero results.
Why the AI does this
Because it works. Calling the API directly from the page is the shortest path to a demo that runs, and the model is optimising for your prompt, not for what happens after launch. Nothing in a one-page prototype tells it a stranger will read the source. You have to say it, once, in your CLAUDE.md or project rules: “Never ship API keys to the browser. All third-party calls go through server routes.” Then it stops happening.
No need to comment or DM me about this one. Everything you need is on this page.