Bolt.new writes frontend-first code, and a frontend has no place to hide a secret. So keys end up in the browser bundle, where anyone can read them. This is the calm 10-minute fix: find the key, move it behind a server function, then kill the old one.
Start with step 1 → Or scan your live site (free)Your app's "bundle" is the JavaScript file your users download when they open your site. If a key is in there, it is public. No hacking required: it is sitting in a file your site hands to every visitor.
Here is how to check, on your own deployed app:
Cmd+Option+F on Mac or Ctrl+Shift+F on Windows.sk-
sk_live_
sk-ant-
AKIA
service_role
What they mean: sk- is the OpenAI key prefix (and a few others), sk_live_ is a Stripe live secret key, sk-ant- is an Anthropic key, AKIA is an AWS access key ID, and service_role is the name of the Supabase key that bypasses your database's row-level rules (that one has its own page).
If any of these turn up next to a long random string, you have your answer. Do not stop at the first hit. Search all five, because a project that leaked one key usually leaked its neighbors too.
Prefer not to dig through files? Our free scanner checks a live site for this kind of exposure. It is passive and needs no signup.
This is not carelessness on your part. It is how frontend-only apps work.
When Bolt generates an app that calls OpenAI or Stripe straight from the browser, the key has to travel with the code, or the call would fail. There is no server in the picture to hold the secret. So the key gets baked into the bundle, and the bundle is public by definition.
Environment variables do not save you here either. Many Bolt apps are built with Vite, and in Vite, any variable whose name starts with VITE_ is copied into the browser bundle at build time. That is by design: frontend env vars are configuration, not secrets. Renaming a hardcoded key to VITE_OPENAI_KEY just moves where the plaintext lives.
To be fair to Bolt: it does have a real place for secrets today. Bolt projects include a Secrets tab (behind the database icon in the project), and values stored there are readable only by server functions, never by the browser. Bolt will even prompt you to add a secret when you ask it to integrate a provider. The leak happens when the app has no server piece at all, or when a key gets pasted into the chat and hardcoded before that flow kicks in.
So the fix is architectural, and it is small.
The pattern is simple: the browser never talks to the provider. It talks to a small endpoint you own. That endpoint holds the key and forwards the request. People call this a proxy or an edge function; it is really just a middleman that keeps the secret out of the bundle.
VITE_ variable. Rebuild, redeploy, then repeat the search from step 1 on the live site. The prefixes should be gone.Do not stop here. Moving the key protects your new setup. It does nothing about the old key, which already sat in public. That is the next step, and it is the one everyone skips.
The old key is burned. It was in a file served to every visitor, and automated scrapers collect exposed keys from public sites and repos as a matter of routine. Treat it as copied, because you cannot prove it was not.
Rotating means: go to the provider's console, revoke or roll the leaked key, create a replacement, put the replacement in the Secrets tab from step 3, and confirm the old key now fails with an authentication error. Our leaked-key runbook has the per-provider rotation table with the exact console page for OpenAI, Anthropic, Stripe, AWS, GitHub, Google, and Supabase.
One caution about waiting for the provider to handle it. Some providers do scan public code for their own key formats: OpenAI disables keys it finds and emails you, Stripe notifies you and may deactivate an exposed key but does not promise to, and GitHub emails you when its secret scanning spots a known key format in a public repo. Treat any of those emails as confirmation that the key was public, not as the fix. Rotate anyway.
If you use Claude Code, our free open-source plugin includes /lictor-rotate, a guided walkthrough of exactly this, provider by provider:
/plugin marketplace add Raffa-jarrl/Lictor-AI
/plugin install lictor-security-suite@lictor-ai
The key may have been public since the day you deployed, not since the day you noticed. Check usage from your launch date forward.
Open the usage page in each provider's console (platform.openai.com/usage, console.anthropic.com). Look for volume you did not generate and models you do not use. Then set or lower spending caps where the provider offers them, so the next mistake has a ceiling.
Check payments for charges or refunds you do not recognize, then the API request logs, which show the IP behind each request.
Search CloudTrail for calls made with the leaked key, and check billing for new charges. Stolen AWS keys are routinely used to spin up compute at your expense.
Email the provider's support with a short factual timeline: when the app went live, when you rotated, which usage is not yours. Some providers forgive first-time fraudulent usage. None of them promise it, but it is always worth the ask.
A leaked key rarely travels alone. The same build that exposed it may be serving an .env file or another credential you have not spotted yet.
So finish the ten minutes with a check. Run the free scan against your live site: passive, no signup. For the codebase itself, the plugin above runs a 48-check audit catalog covering leaked keys, exposed configs, and the rest of what gets apps breached, reported in plain English.
And because "trust us" is not evidence: we publish a reproducible benchmark. In a blind run on a 30-case stratified sample of OWASP Benchmark 1.2, the audit scored an F1 of 93.8%. That is a sample, not the full benchmark, and we say so right in the results.
Same disease, same cure. The find-move-rotate loop above works for any AI-built app; only the consoles change.
Replit gives you more guardrails than Bolt, but they are opt-in. Its Secrets pane stores values encrypted and hands them to your app as environment variables, which is the right home for a key. Two things it does not do for you: it does not move a key you hardcoded into Secrets automatically, and per Replit's docs, secrets are not available to static deployments. A static deployment is plain frontend files, so any key inside one is public, exactly like a Bolt bundle. When you publish, also confirm your secrets actually made it into the deployed environment's configuration, then run the step 1 search on the published URL.
Cursor is an editor, not a host, so there is no secrets manager at all. Whatever code you accept is what ships. If that code puts a provider call with a key inside a React component, the key rides the bundle like any other string. The fix is the same proxy pattern from step 3, with the key living in your host's environment variables, followed by a rotation.
Building with Lovable or another AI app builder? Our security checklist for AI-built apps covers the leaks these tools tend to create before launch day.
Open your deployed site, open the developer tools, and search across all loaded JavaScript files for the prefixes sk-, sk_live_, sk-ant-, AKIA, and service_role. The key usually hides in the built bundle, not the HTML source. If a prefix appears next to a long random string, that credential is public. The free scan checks for this too, passively and with no signup.
Only half of it. A server-side secret protects the new key going forward. The old key already sat in a public bundle and should be treated as copied, so it still has to be rotated at the provider. And remember that frontend env vars are not secrets: in a Vite app, anything named VITE_* is compiled into the bundle on purpose.
They can. Replit has an encrypted Secrets pane, but it will not move hardcoded keys there for you, and its docs say secrets are unavailable to static deployments, which are public files. Cursor has no secrets manager, so whatever code you accept is what ships. The loop is the same everywhere: find, move server-side, rotate.
You found this leak by accident, or worse, by bill. Lictor's continuous monitoring re-checks your live site around the clock and emails you when something new is exposed, from $49/mo. It is run by a security engineer with 20 years in the field who does real responsible-disclosure work. Plans are on the business page, and you can sign up here.