The AI put your API key in the frontend: OpenAI, Stripe, or Supabase keys visible in the JS bundle
Symptoms
- View-source or the DevTools Network tab shows your raw OpenAI, Stripe, or service-role key in the shipped JavaScript
- Mystery usage on your account, or your quota is suddenly exhausted
- A surprise bill: one documented case ran to $14,000 on OpenAI after attackers found an exposed key and maxed out usage
- A secret-scanning alert email lands in your inbox
Why it happens
AI pattern-matches quick-start examples
Asked to 'call the OpenAI API from my app', AI tools reproduce tutorial code, which puts the secret key directly in client code. Quick-starts are written for local experiments, not production, and the model does not know the difference.
Frontend-only platforms have nowhere else to put the key
On vibe platforms with no backend, the key has nowhere else to live, so it ships in the bundle by construction. Any working integration on such a platform is a leaking one.
This failure is systemic, not rare
Escape.tech scanned 5,600+ vibe-coded apps and found 400+ exposed secrets in frontend bundles. Wiz Research found systemic risks in roughly 20% of organizations building on vibe-coding platforms, including hardcoded OpenAI keys and service-account credentials in client-side JavaScript.
How to fix it
- Rotate the key first (this is the security-critical step)Treat any key that ever shipped in a bundle as compromised. Revoke and rotate it immediately in the provider dashboard. Removing it from the code does not un-leak it; assume it has already been scraped.
- Move the API call server-sidePut the call behind a Next.js API route, a Supabase Edge Function, or a small backend proxy. The secret lives in a server environment variable, and the browser only ever talks to your endpoint.
- Add auth to the proxyAn open proxy is just a free relay for your key. Require a session or token before the endpoint will spend your money. Missing auth and missing rate limiting compound this exact failure, so close both.
- Know which keys are allowed client-sideOnly intentionally public values belong in the browser: Stripe publishable keys, Firebase web config, and the Supabase anon key (safe only with RLS enabled). Anything named 'secret', 'service', or 'private' is server-only.
- Cap the damage for next timeSet hard spend caps and budget alerts on every paid API, and add secret scanning (GitHub push protection, gitleaks, GitGuardian) so a regenerated key never ships again.
How to prevent it
- Never put a secret in any file the frontend imports; secrets live in server environment variables only.
- When wiring any paid API, ask the AI explicitly for a server-side proxy pattern, then verify where the key ends up.
- Run secret scanning (GitHub push protection, gitleaks, or GitGuardian) on every commit.
- Set spend caps and billing alerts before launch, not after the first scary invoice.
- Periodically search your shipped JS bundle for provider key names and the word 'secret'.
Tools that actually fix this
Recommended because they address the failure mode above, not because of the payout. Some are affiliate links; see how we choose.
A purpose-built secret scanner covering 450+ key types. Its ggshield pre-commit hook blocks a key from ever being pushed, and history scanning finds the ones you already leaked. Free for individuals.
Try GitGuardian →All-in-one appsec (code scanning, dependencies, secret detection, containers) with low-noise output, built for small teams that cannot run five separate tools. One dashboard covers several failure modes.
Try Aikido Security →Frequently asked questions
Is it safe to put an API key in a React app?
No secret key is safe in a React app or any frontend code: everything in the JavaScript bundle is readable by anyone with DevTools. The only values that belong client-side are intentionally public ones, such as Stripe publishable keys, Firebase web config, and the Supabase anon key with RLS enabled. Secret keys for OpenAI, Stripe, or a Supabase service role must stay on a server.
I deleted the exposed API key from my code. Am I safe now?
No. A key that ever shipped in a bundle must be treated as compromised, because scrapers may already have copied it. Revoke and rotate the key in the provider dashboard first, then move the API call behind a server endpoint so the new key never appears in client code.
How much damage can an exposed API key actually do?
A lot, and quickly. One documented case ran to a $14,000 OpenAI bill after attackers found an exposed key and maxed out usage. Escape.tech found 400+ exposed secrets while scanning 5,600+ vibe-coded apps, so attackers actively hunt for exactly this. Spend caps and billing alerts limit the blast radius when prevention fails.
How do I call the OpenAI API from my app without exposing the key?
Route the request through your own server: a Next.js API route, a Supabase Edge Function, or a small backend proxy. The secret key lives in a server environment variable, the browser calls your endpoint, and your endpoint calls OpenAI. Add authentication and rate limiting to that endpoint so it cannot be abused as a free relay for your key.
Sources
- Common security risks in vibe-coded apps (Wiz Research)
- How we discovered vulnerabilities in apps built with vibe coding (Escape.tech)
- OpenAI API key exposure (Rafter)
- How we discovered over 2k high-impact vulnerabilities in vibe-coded apps (Security Boulevard)
Related errors
Lovable API key exposed: secrets visible in your client-side code
Replit API key exposed: secrets leaking from a public Repl
Accidentally committed an API key to GitHub
Why is my OpenAI API bill so high? No rate limiting on your AI endpoints
Supabase Row Level Security is off: your whole database is readable with the anon key
The Vibe Oops briefing
One email when something ships to production that should not have. New incidents, new error guides, no filler.