Fix: Bolt × security

Bolt put your app in a browser tab.
Make sure your keys aren't in there too.

A securityscanner.dev audit found 15% of Bolt apps exposing hardcoded API keys in their frontend code. And exposed keys don't sit idle: bots scrape freshly deployed bundles within hours, and a leaked OpenAI key turns into someone else's usage bill. None of this means your product idea was wrong. It means the demo pattern shipped — and demos keep secrets in places production never can.

Why Bolt apps leak: the shortest path to a working demo

Bolt builds and runs your app inside WebContainers — everything executes in the browser, which is exactly why it feels instant. But that architecture nudges generated code toward client-side everything: when you ask Bolt to integrate OpenAI or Stripe, the fastest working version puts the key right in the frontend code, and that's frequently what you get. vibeappscanner.com documents the recurring vulnerabilities in almost every Bolt app it audits: exposed Supabase service_role keys, missing RLS policies, and bolted-on auth that renders a login screen without the API ever verifying who's calling. Unescaped user input adds stored XSS to the list. Each one is invisible in the preview. All of them are trivially findable by anyone who opens devtools on your deployed app.

The Bolt security checklist — run it before someone else does

Search your bundle for keys

Deploy, view source, search for 'sk-', 'sk_live', and 'service_role'. Bolt frequently hardcodes third-party keys in frontend files because that's how the demo worked. Every hit needs rotation, not just deletion.

The service_role key in client code

Supabase's service_role key bypasses every RLS policy. If it appears anywhere the browser can reach, your database has no access control at all — regardless of what policies you wrote.

Auth that only exists in the UI

A login page proves nothing. Call your API endpoints directly with curl, unauthenticated. If data comes back, your auth is decoration — Bolt often gates the interface without gating the API.

Unescaped user input

Bolt-generated components frequently render user content without sanitization. One stored XSS in a comment field means an attacker runs JavaScript in every other user's session.

Nothing slowing an attacker down

Try 30 rapid wrong-password logins against your own app. If nothing throttles you, credential stuffing runs at full speed against every account you have.

The wall: a server that refuses to start without its secrets

On useDeploy, secrets aren't scattered through source files — they live in environment variables, and a Zod schema validates all of them at boot. Miss one and the process exits with a named error instead of limping into production. And because secrets only exist server-side, the client bundle physically cannot leak what it never receives.

terminal — boot with a missing secret
 1  $ bun run start
 2  ✗ Invalid environment:
 3      BETTER_AUTH_SECRET   Required
 4      DATABASE_URL         Required
 5  error: process exited with code 1
 6  
 7  # secrets are validated at boot, server-side only.
 8  # the bundle can't leak what the client never receives.

What's already handled on the base

Boot-validated secrets

One Zod-validated env schema is the single home for every credential. Nothing secret is importable from client code, so 'key in the bundle' stops being a possible bug class.

Billing webhooks that survive replays

Payment webhooks are idempotent by externalOrderId on top of a durable outbox — a provider retrying the same event twice can't double-charge or double-credit anyone.

Signed outgoing webhooks

Webhooks your app sends are HMAC-signed, so receivers can verify they came from you — the discipline you want on both sides of the webhook pipe.

Rate limiting by tier

Read, write, and auth traffic each get their own limiter, with auth bucketed by both IP and email. The 30-wrong-passwords test from the checklist above hits a 429 here.

Isolation that isn't optional

Org-scoped repositories and a Prisma query guard make cross-tenant reads return 404. There is no equivalent of 'forgot to enable RLS' because there's no switch to leave off.

A test suite watching all of it

450+ automated tests plus a Playwright E2E gate in CI exercise the auth flows, webhook idempotency, and tenant boundaries on every merge.

Migration path: from WebContainer to a real backend

Export your project from Bolt — download the code or push it to GitHub. The React UI travels well; it's the everything-in-the-browser architecture that doesn't. The migration is mostly a relocation: every fetch that calls a third-party API with an embedded key becomes a call to your own typed endpoint, and the key moves to server-side env where the Zod schema validates it at boot. Business logic that lived in components becomes use cases behind Express controllers, typed end to end via the generated OpenAPI contract. Rotate every key that ever appeared in Bolt-generated source — assume all of them leaked — and do the move one integration at a time: OpenAI first if that's your burn risk, Stripe next, then the rest. Your users see the same app. Your keys stop being public.

Bolt × security, asked directly

My OpenAI key already leaked. What do I do first?+
Rotate it now, before any refactor — revoke the old key in the OpenAI dashboard and set spend limits. Then check billing for unfamiliar usage. Only after the key is dead is it worth moving the call server-side so the new one can't leak the same way.
Can I keep Supabase while I migrate?+
Yes — run both during the transition. Keep RLS enabled on anything still served by Supabase, and move tables into the base's Prisma schema feature by feature. The tenant guard takes over as each table crosses.
Does the base handle Stripe webhooks properly?+
Yes, and this is a place AI-generated handlers routinely fail: providers resend events, and non-idempotent handlers double-process them. The base dedupes by externalOrderId over a durable outbox, so replays are absorbed.
Is this a rewrite?+
The frontend isn't — components and styling port over. The backend effectively is, and that's the point: the browser-resident backend is the part leaking keys. On the base you're not rebuilding auth, billing, or tenancy though; you're plugging your product logic into ones that exist.

One license. Two ways to own it.

CORE

Founder

$99one-time

A frozen snapshot of UseDeploy. Yours forever, no updates.

  • Frozen-version zip download
  • Full source, commercial use, unlimited projects
  • All 800+ tests · all docs pages
  • 14-day refund
RECOMMENDED

LATEST + UPDATES

Lifetime

$199$249one-time

Always the latest UseDeploy. Re-download every release, free.

  • Latest-version zip — re-downloadable forever
  • Every future release at no extra charge
  • Priority Discord support
  • All 800+ tests · all docs pages
  • 14-day refund

Ship the app without shipping your keys

Keep the speed that got you here. Move onto a base where secrets are boot-validated, webhooks are idempotent, and 450+ tests guard the floor.