Rescue · Security

Your API Keys Are Probably in the Bundle

OpenAI keys, Stripe secret keys, Supabase service-role keys — AI builders put them straight into frontend code constantly, because it’s the shortest path to a working demo. Anyone can open DevTools and read them. Five-minute check, then the proper fix.

Severity: criticalRotation: minutesRefactor: 1–2 days
Symptoms

How exposed keys surface

A four-figure OpenAI bill

Scrapers harvest keys from public bundles and GitHub within hours and resell the capacity. The bill is usually how people find out.

sk- strings in view-source

If you can see it, so can everyone. The browser bundle is public by definition — that’s what shipping a frontend means.

Provider emails about unusual activity

OpenAI and Stripe scan public repos and traffic patterns. If they noticed, assume others noticed first.

Charges or data access you didn’t make

A leaked Stripe secret or service-role key isn’t a cost problem — it’s full account control. Treat it as an incident, not a bug.

Frontend calls are easier to generate

Calling OpenAI from the browser takes four lines; doing it properly needs a server route. Guess which one the model reaches for when you say 'add AI to my app'.

Env vars aren't automatically secret

Anything prefixed VITE_ or NEXT_PUBLIC_ is compiled into the public bundle by design. The AI knows the prefix makes the variable work — not that it makes it public.

Nothing in the demo objects

The key works from the browser. The app runs. Every incentive during prompting says done — the cost arrives weeks later on someone else's schedule.

## The five-minute check
# build, then search the output for secrets
npm run build && grep -rE "sk-[a-zA-Z0-9]|service_role|whsec_|api[_-]?key" dist/ .next/ 2>/dev/null
# check the deployed site too: view-source + network tab
# any Authorization header going straight to api.openai.com from the browser = exposed
# and the repo history — deleted from code is not deleted from git
git log -p | grep -E "sk-|service_role" | head

Find, rotate, restructure

01

Rotate First

Every found key is compromised — revoke and reissue at the provider now, before touching code. Set hard usage caps where offered; OpenAI's is free insurance.

02

Move Calls Server-Side

Browser → your endpoint (with the user's session) → provider. The key lives in an edge function or server route; the browser never sees it.

03

Add the Gate

Your endpoint now authenticates users, rate-limits and logs — three protections a direct browser call can never have.

04

Scrub the History

Keys committed to git live in history even after deletion. If the repo was ever public or shared, rotate on that basis alone.

05

Make It Stick

Secrets in the host's manager (Netlify/Vercel), per-environment .env files, and a pre-deploy grep for sk- so this class of bug can't ship twice.

FAQ

Common questions

Which keys are safe to have in the browser?

Ones designed for it: Supabase anon key (with RLS on), Stripe publishable key, analytics IDs. The test is the name — anything called secret, service or private is not frontend material, ever.

I rotated the key. Am I done?

You've stopped the bleeding. If the code still calls providers from the browser, the new key ships in the next build. The server-side refactor is the actual fix — rotation buys the time to do it.

My OpenAI bill spiked. Is that this?

Almost certainly — harvested keys are resold within hours. Rotate, set a hard usage cap, and check the usage log to bound the damage window.

The key was only in an old commit — does that count?

Yes. Git history is part of the repo; anyone who cloned or forked it has the key. Rotation is the only fix history allows.

Can you do the whole cleanup for us?

Yes — key hygiene is part of every rescue's security pass: found, rotated, moved server-side and gated, with the pre-deploy check installed so it stays fixed.

Keep reading

Related rescue guides

$299 triage · 48 hours · credited against any fix

Get the secrets out of the bundle

Key exposure is checked in every triage — found, rotated and refactored server-side as part of the security pass.

43+ happy clients · rated 4.9/5 · senior engineers on every build