AI wired up your AI feature, your payments, your database — and it put the secret keys right in your frontend. Which means anyone who opens your site can read them. Press F12, they’re there.
What you shipped
The giveaway is the variable name. In Next.js:

Anything prefixed NEXT_PUBLIC_ is baked into the bundle the browser downloads. So a server-only secret — your AI key, your Stripe secret, your Supabase service-role key — ends up downloadable by every visitor.
How anyone exploits it
They don’t even have to attack you. View source, or open the Network tab, and read the key. With your AI key they run up your bill; with your service-role key they own your database (it bypasses Row Level Security entirely).
Why you won’t catch it
Your app works perfectly while this is true — the key is right there, so the feature functions. Nothing errors, nothing warns. The exposure is invisible until someone uses it.
Why AI does it
The fastest way to make a feature “work” is to call the service directly from the component. To do that in the browser, the key has to be in the browser — so AI reaches for the NEXT_PUBLIC_ prefix. The prefix that makes it work is the same prefix that leaks it.
The fix
Keep secrets on the server. Call the service from an API route and return only the result:

// server route only — no NEXT_PUBLIC_
const key = process.env.OPENAI_KEY
And rotate any key that has ever shipped to a browser — assume it’s already burned.
Check your app
- Grep for
NEXT_PUBLIC_(orVITE_,PUBLIC_) and confirm none of them are secrets. - Build the app and search the output bundle for
sk-,service_role, and your key prefixes. - Any key that was ever public gets rotated, not just moved.
The bigger problem
A senior dev knows which keys can touch the browser and which can’t. But if nobody senior reads the code, the exposed key ships — the feature works, so it looks done. The author and reviewer are the same model with the same blind spot.
That’s the gap Velify is built to close: it reads your project and flags exactly this, in plain language, no terminal.
