Bolt.new put your API keys in the browser — how to fix it
Bolt.new's default is to hardcode secrets into the client bundle, where anyone can read them — here's how to check your app and move every key to the server where it belongs.
If you shipped an app with Bolt.new, there is a good chance your API keys are sitting in the browser right now, readable by anyone who opens DevTools. This is not a rare mistake — it is the default behavior. AI builders optimize for a working demo, and the fastest way to make an OpenAI call, a Stripe charge, or a Supabase query work in a preview is to paste the key straight into the frontend code. It runs, the demo looks perfect, and the secret ships to every visitor. This matters because insecure defaults are the norm, not the exception: across AI-built apps roughly 91.5% carry at least one security flaw, and a scan of 5,600 such apps by Escape.tech turned up over 400 exposed secrets. Bolt's signature failure mode is being one of them.
Why Bolt.new leaks keys by default
Bolt.new generates client-side apps — typically a Vite or Next.js frontend that runs in the user's browser. Anything in that bundle is public. Two habits cause the leak. First, the key gets hardcoded directly into a component or a fetch call. Second, and more subtle, the key is stored in an environment variable that gets inlined into the client build. In a Vite app, any variable prefixed with VITE_ is bundled into the JavaScript that ships to the browser; in Next.js, the same is true of any variable prefixed with NEXT_PUBLIC_. Bolt frequently names a secret VITE_OPENAI_API_KEY or NEXT_PUBLIC_STRIPE_SECRET_KEY, which feels like an environment variable but is actually a public string. The rule is simple and absolute: if the code that reads the key runs in the browser, the key is not secret, no matter what you named the variable.
How to check your app in five minutes
You do not need a security background to find this. Open your deployed app in a browser, open DevTools, and go to the Network tab; reload the page and look at the JavaScript files being downloaded. Use the browser's search across sources (in Chrome DevTools, press Ctrl+Shift+F / Cmd+Option+F) and search for the beginning of a known key prefix — sk- (or sk-proj-) for OpenAI, sk_live_ / sk_test_ / rk_live_ for Stripe secret keys, AIza for Google, or your Supabase project's service_role token. If any match appears in a file the browser downloaded, that key is public. Do the same in your source: search the whole repo for the literal key, and grep for VITE_ and NEXT_PUBLIC_ variable names that hold anything you consider a secret. Treat every key you find this way as already compromised.
- Rotate first: revoke the exposed key in the provider dashboard and issue a new one — assume the old one is already scraped
- Move each secret out of the frontend and into a server: an API route, an edge function, or a small backend the browser calls instead of the provider
- Rename client-safe values only; never put a secret behind VITE_ or NEXT_PUBLIC_ prefixes
- Add .env to .gitignore and confirm no key was ever committed (check your Git history, not just the current files)
- Set spending limits and per-key usage alerts on OpenAI, Stripe, and any paid API as a blast-radius cap
- Re-run the Network-tab check after redeploying to confirm the key no longer appears in any downloaded file
The real fix: put the secret on the server
Rotating a leaked key stops the bleeding, but the new key leaks the same way unless you change where it lives. The durable fix is architectural: the secret must only ever be read by code the user cannot see. Instead of the browser calling OpenAI or Stripe directly with the key attached, the browser calls your own server endpoint — a Next.js API route, a serverless or edge function, or a Supabase Edge Function — and that server-side code holds the key, makes the third-party call, and returns only the result. The key stays in a server-only environment variable (no VITE_ / NEXT_PUBLIC_ prefix) that is never bundled into the client. This also gives you a natural place to add the things Bolt skipped: authentication on the endpoint, rate limiting, and input validation, so a stranger cannot run up your OpenAI bill even after the key is safely hidden.
A key in the browser is not a bug you patch once — it is a design decision the tool made for you. Fixing it means moving the secret to the server, not hiding it better on the client.
Verify, then keep it that way
After you have moved every secret server-side and redeployed, prove it. Repeat the Network-tab and source-search checks and confirm no key prefix appears in any file the browser downloads. Then keep it from regressing: every time you ask Bolt for a new feature that touches a third-party API, it may reintroduce a client-side key, so make the DevTools check part of your release routine rather than a one-time cleanup. The pattern is consistent across AI builders — Bolt leaks secrets, Cursor tends to ship broken object-level authorization (BOLA), Lovable often ships without Supabase Row-Level Security, and Replit deployments have exposed .env files — but they share a root cause: the tool proves the feature works and stops there, leaving the security to you.
If you would rather not audit and re-architect this yourself, Comestare's senior software and security engineers will find every exposed key, move your secrets server-side, and ship the hardened version for you.