ISSUE 37 · 6 MIN READ ·

Your vibe-coded app is shipping its API keys to the browser

An AI-assisted app can bake your OpenAI or cloud key straight into the frontend, where anyone can read it. How to check your own site in ten minutes.

Someone on the team needed a small tool — a page that summarises support tickets, say, or drafts replies — and built it fast with an AI coding assistant. It works. It went live. Everyone moved on. The assistant, being helpful, wired the app up to call OpenAI (or a cloud service) directly, and to do that it needed the secret key. So it put the key where the code that runs could reach it: in the frontend, the part that gets sent to every visitor’s browser.

That’s the quiet failure. The app looks completely normal from the outside — it does its job, nothing is broken, nothing is on fire. But the secret key that bills to your account is now sitting in a file that anyone can read by opening their browser’s developer tools. Bots trawl the web for exactly this, and they are quick: a leaked key can start racking up charges, or be resold, within minutes of going live.

Why it stays invisible

A web app has two halves. The backend runs on a server you control, where secrets can be kept out of sight. The frontend is everything sent to the visitor’s browser — the HTML, the styling, and the JavaScript that makes the page work. Anything in the frontend is, by definition, handed to every single visitor. There is no such thing as a secret in code that runs in someone else’s browser; it only feels hidden because you don’t normally look.

The reason AI-assisted builds trip over this so often is that calling a paid service from the frontend is the shortest path to a working demo. The assistant writes code that talks to the API directly from the browser, drops the key into a variable or an environment file that gets bundled in, and the app runs first time. The proper arrangement — the browser talks to your server, and only your server holds the key and talks to the paid service — is more code and one more moving part, so unless someone asks for it, it often doesn’t get built.

Nothing warns you. The key works, so the app works. The bundle that carries it is minified into an unreadable wall of text, which lulls people into thinking it’s hidden — but “hard to read” is not “secret”, and the tools that scrape for keys don’t find them by reading, they find them by pattern-matching. A string that starts sk- is a lit beacon.

Find it yourself

You can check your own site in about two minutes, and you don’t need to be the person who built it.

  1. Open the site in Chrome or Edge, on the page that does the clever bit — the one that talks to the AI or cloud service.
  2. Open developer tools. Press F12, or right-click anywhere on the page and choose Inspect.
  3. Search everything the page loaded. Press Ctrl+Shift+F (on a Mac, Cmd+Option+F) to open the search box that looks across all the page’s files. Type a telltale prefix and see what comes back:
    • sk- for OpenAI and many others
    • AKIA for Amazon Web Services
    • AIza for Google Cloud and Firebase
    • or just search key, secret, token and read what turns up.
  4. Check the network traffic too. Switch to the Network tab, reload the page, use the feature once, and look at the requests going out. If the browser is calling api.openai.com (or another paid service) directly, the key to authorise that call has to be in the browser — so it’s exposed, even if step 3 didn’t spot it.

If you’d rather not poke around yourself: on any page, right-click and choose View Page Source, then use your browser’s find (Ctrl+F) for the same prefixes. It won’t catch keys hidden in separate script files, but it catches the most careless cases in seconds.

Finding one is a slightly sinking moment. The good news is that it’s completely fixable, and finding it before a bot does is the whole game.

The fix

The rule is simple to state: a secret key must never leave your server. The browser should never hold it, which means the browser should never call the paid service directly.

Route the call through your own backend. The browser asks your server to do the work; your server holds the key, calls OpenAI or the cloud service, and passes the result back. The key stays on the server, out of every visitor’s reach. Most hosting platforms make this straightforward — a small serverless function is enough — and it’s worth asking your AI assistant to rebuild it this way explicitly, because it usually won’t unless told.

Then rotate the exposed key immediately. A key that has been in a public bundle should be treated as burned, even if you think no one saw it. Go to the service’s dashboard, revoke the old key, and issue a new one that lives only on the server. Rotating without moving the call server-side just leaks the new key too.

Put a ceiling on the damage. Set a hard spending limit or budget alert on the account, so a leak that slips past you can’t run up an open-ended bill. Where the service allows it, restrict the key to the specific things it needs to do, so a stolen one is worth less.

Stop it coming back. Add a quick look at the frontend bundle to whatever passes for your release checklist — the same Ctrl+Shift+F search for sk-, AKIA, AIza. It takes a minute and it’s the sort of thing that only ever bites the people who assumed it couldn’t happen to them.

None of this makes AI-assisted building a bad idea — it’s a genuinely fast way to ship something useful. It just means keeping one boundary sacred: the key stays on your side of the wire, and the ten-minute check above tells you, today, whether it already slipped over.

Check your inbox — confirm and you're in. Latest issue: Your vibe-coded app is shipping its API keys to the browser.

One real, fixable exposure every week. Free.