Bolt.new security: 7 things to verify before sharing the URL.

2026-05-08 · vibecheck team · 7 min read · Platform · Bolt.new

Quick answer Bolt.new (StackBlitz) generates full-stack apps in browser-side WebContainers, then deploys to Netlify or Vercel. The most common failures are: env vars pasted into src/ instead of .env, missing or permissive Supabase RLS, sourcemaps deployed to production, source-routes accidentally exposed at /_routes, default-credentials components scaffolded without auth, CORS wide-open in generated functions, and Stripe wiring that lands the live key in client. This post is the seven-item audit, with the exact commands to test each one.

Bolt is fast. Faster than Lovable for full-stack scaffolds because the WebContainer runs the dev server, runs the migrations, and shows you the live preview without leaving the browser. The downside: it's also faster at making the kind of mistakes that ship straight to production. Here's the audit.

1. Env vars in src/, not in .env

Bolt's prompt-driven file editor often inlines API keys directly into source files when you say "wire up Stripe / Supabase / OpenAI." The keys end up in src/lib/supabase.ts or src/api.ts rather than in .env with a Vite/Next prefix.

Test:

find src -type f \( -name '*.ts' -o -name '*.tsx' -o -name '*.js' -o -name '*.jsx' \) \
  | xargs grep -lE '(sk_live_|sk-(proj-)?[A-Za-z0-9]{20}|eyJ[A-Za-z0-9_-]+\.eyJ)'

Fix: move keys to .env; in Vite use import.meta.env.VITE_* only for keys that are safe in the browser; keep server-only keys without the VITE_ prefix and reference them only in API routes / Edge Functions.

2. Supabase RLS — the standard issue

Bolt's templates wire @supabase/supabase-js with the anon key but don't generate RLS policies. Your client can read every table with the anon key.

Test from outside:

curl 'https://<ref>.supabase.co/rest/v1/users?select=*&limit=5' \
  -H 'apikey: <your-anon-key>' \
  -H 'Authorization: Bearer <your-anon-key>'

If you get rows back, the table is open. Run a vibecheck scan for the autofix RLS SQL, or read the response guide.

3. Sourcemaps deployed to production

Vite's default is to emit *.map files in dist/. Bolt deploys what's in dist/, sourcemaps and all. That gives any visitor the full unminified source — including comments, credentials that survive minification, internal logic.

Test:

curl -I https://your-app.netlify.app/assets/index-AbCdEf12.js.map

If the map returns 200 with a JSON body, your unminified source is public. Vite config:

// vite.config.ts
export default defineConfig({
  build: { sourcemap: false }   // production: false; only enable for debugging
});

4. Generated API functions with permissive CORS

When Bolt generates a Netlify or Vercel serverless function, the default template often includes:

headers: {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Credentials': 'true',
}

That combination is forbidden by browsers but accepted by some non-browser clients and proxies. It also indicates the dev didn't think about origin restrictions.

Test:

curl -I https://your-app/api/<function> -H 'Origin: https://attacker.example.com'
# Look for: Access-Control-Allow-Origin: * combined with Allow-Credentials: true

Fix: set ACAO to your actual frontend origin (or use an allowlist), not *. If you don't need cookies/auth across origins, drop Allow-Credentials entirely.

5. Stripe wiring with sk_live_ in client

If you said "add Stripe checkout" while building, Bolt likely accepted whatever Stripe key you provided and wired it client-side. If you pasted sk_live_ instead of pk_live_, that secret key is now in your bundle.

Test:

curl -sL https://your-app/ | grep -oE 'sk_live_[A-Za-z0-9]{24,}'

If anything matches: rotate immediately at dashboard.stripe.com/apikeys. The publishable key (pk_live_) is the one that belongs in the browser; the secret key (sk_live_) only ever lives in your server-side function.

6. WebContainer state leaked in deploy artifacts

Bolt's WebContainer persists state — including node_modules, .cache, sometimes .env — into the deploy directory if not explicitly excluded. Look for these in your deployed output:

curl -I https://your-app/.env
curl -I https://your-app/node_modules/some-pkg/package.json
curl -I https://your-app/.bolt/state.json
curl -I https://your-app/_routes  # Vite's route manifest

Any 200 here is bad. Update your deploy ignore list (.gitignore, netlify.toml publish dir, vercel.json's output config) so only dist/ ships.

7. Routes meant for development still exposed

AI scaffolds frequently include /admin, /debug, /test, or /dev routes that were created for testing and never removed. Bolt's preview keeps these alive in production unless you tell it to.

Test:

for path in admin debug dev test internal staging _admin api/admin; do
  echo -n "$path: "; curl -sIo /dev/null -w '%{http_code}\n' "https://your-app/$path"
done

Anything returning 200 should either be deleted or gated behind authentication. The vibecheck scanner probes most of these automatically.

One command does items 2–7

Items 1 (env vars in src/) requires looking at your source code; we can't see that from outside. Items 2 through 7 are exactly what the vibecheck scanner tests against any deployed URL.

Inspect your Bolt.new app