Bolt.new is fast. Describe an app, watch it generate the entire stack, frontend, backend, database, and deploy it. The whole process takes minutes.
That speed is the problem. When something goes from idea to deployed in five minutes, there's no moment where someone stops and asks: "wait, is this actually secure?"
The answer, based on what we see in scans, is almost always no.
Bolt.new apps share the same systemic security issues that Wiz identified across vibe coding platforms: passwords in JavaScript, API keys in client code, databases wide open, and internal tools deployed without auth. Veracode's research puts the number at 45% of AI generated code containing OWASP Top 10 vulnerabilities.
This guide covers the Bolt.new specific patterns and how to fix each one. For the broader picture of AI coding security, see our [complete vibe coding security guide](/learn/vibe coding security).
Issue 1: Default Supabase setup is wide open
Bolt.new loves Supabase. It's the default backend for most generated apps. The problem: Bolt.new sets up tables quickly and often skips Row Level Security, or enables it with policies that allow all operations.
Here's a typical Bolt.new database setup:
-- Bolt.new generated migration
CREATE TABLE posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id),
title TEXT NOT NULL,
content TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- RLS? Maybe. Restrictive policies? Almost never.
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Enable all access" ON posts FOR ALL USING (true);
That last line makes RLS meaningless. Every user, authenticated or not, can read, insert, update, and delete every row. Your Supabase anon key (which is in the client) becomes a skeleton key.
The fix
Replace blanket policies with scoped ones:
-- Drop the open policy
DROP POLICY IF EXISTS "Enable all access" ON posts;
-- Public can read published posts
CREATE POLICY "Anyone can read posts" ON posts
FOR SELECT
USING (true); -- OK for public content
-- Only the author can insert
CREATE POLICY "Users create own posts" ON posts
FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Only the author can update
CREATE POLICY "Users update own posts" ON posts
FOR UPDATE
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- Only the author can delete
CREATE POLICY "Users delete own posts" ON posts
FOR DELETE
USING (auth.uid() = user_id);
Do this for every table. Open your Supabase dashboard, click through each table's policies, and look for USING (true) on INSERT, UPDATE, or DELETE operations.
Issue 2: Missing security headers in Vite config
Bolt.new generates Vite based React or Svelte apps. Vite's default config includes zero security headers. No Content Security Policy, no X Frame Options, no HSTS. Nothing.
This means your app is vulnerable to:
- XSS attacks: no CSP to block injected scripts
- Clickjacking: no frame protection, so your app can be embedded in a malicious iframe
- MIME sniffing: browsers may interpret uploaded files as executable content
The fix
For development, add headers to vite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
headers: {
'Content-Security-Policy': "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://*.supabase.co",
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',
},
},
});
For production, configure headers on your hosting platform. On Netlify, add a netlify.toml:
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
Permissions-Policy = "camera=(), microphone=(), geolocation=()"
Strict-Transport-Security = "max-age=31536000; includeSubDomains"
Content-Security-Policy = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://*.supabase.co"
On Vercel, use vercel.json:
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains" }
]
}
]
}
Issue 3: Exposed environment variables
Bolt.new uses Vite, and Vite has a rule: any environment variable starting with VITE_ is bundled into client side JavaScript. Bolt.new frequently puts sensitive values behind this prefix.
# .env generated by Bolt.new
VITE_SUPABASE_URL=https://abc.supabase.co
VITE_SUPABASE_ANON_KEY=eyJ... # This one is OK, it's meant to be public
VITE_OPENAI_API_KEY=sk-proj-abc123... # This is NOT OK
VITE_STRIPE_SECRET_KEY=sk_live_... # Definitely not OK
The Supabase anon key is designed to be public (that's why RLS matters). But OpenAI keys, Stripe secret keys, and other third party credentials should never touch the browser.
The fix
- Remove the
VITE_prefix from any secret that shouldn't be client side - Move those API calls to Supabase Edge Functions or a backend API route
- Verify nothing leaked by building and inspecting the output:
# Build the app
npm run build
# Search the output for leaked secrets
grep -r "sk-proj-\|sk_live_\|sk_test_\|secret" dist/
Create a Supabase Edge Function for any third party API call:
// supabase/functions/generate/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
serve(async (req) => {
// Verify the user is authenticated
const authHeader = req.headers.get("Authorization");
if (!authHeader) {
return new Response("Unauthorized", { status: 401 });
}
const { prompt } = await req.json();
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${Deno.env.get("OPENAI_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-4o",
messages: [{ role: "user", content: prompt }],
max_tokens: 500,
}),
});
const data = await response.json();
return new Response(JSON.stringify(data), {
headers: { "Content-Type": "application/json" },
});
});
Then set the secret server side:
supabase secrets set OPENAI_API_KEY=sk-proj-abc123...
Issue 4: No rate limiting on API routes
Bolt.new generates API endpoints and Edge Functions without any rate limiting. Every endpoint is unlimited by default, meaning anyone can hammer your /api/generate endpoint thousands of times and burn through your API credits.
The fix
For Supabase Edge Functions, add basic rate limiting using a simple in memory store or Supabase itself:
// Simple IP-based rate limiter for Edge Functions
const rateLimitMap = new Map<string, { count: number; resetTime: number }>();
function rateLimit(ip: string, maxRequests = 10, windowMs = 60000): boolean {
const now = Date.now();
const entry = rateLimitMap.get(ip);
if (!entry || now > entry.resetTime) {
rateLimitMap.set(ip, { count: 1, resetTime: now + windowMs });
return true;
}
if (entry.count >= maxRequests) {
return false;
}
entry.count++;
return true;
}
serve(async (req) => {
const ip = req.headers.get("x-forwarded-for") || "unknown";
if (!rateLimit(ip)) {
return new Response(JSON.stringify({ error: "Too many requests" }), {
status: 429,
headers: { "Content-Type": "application/json", "Retry-After": "60" },
});
}
// ... rest of your handler
});
For production, use a proper rate limiting solution like Upstash Redis:
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "60 s"),
});
The Bolt.new security checklist
Run through this before deploying any Bolt.new app:
- Scan first: Run your app through AmIHackable to see what's exposed
- Audit RLS policies: Check every Supabase table for
USING (true)on write operations - Search for exposed secrets: Build the app and grep the
dist/folder for API keys - Add security headers: Configure them on your deployment platform
- Add rate limiting: Protect every API endpoint and Edge Function
- Check public routes: Visit
/admin,/dashboard,/api/*while logged out - Re scan: Verify your fixes took effect
Prompt Bolt.new securely from the start
Add this to your initial Bolt.new prompt:
Before generating any code, apply these security requirements:
- Enable Supabase RLS on all tables with user-scoped policies (no USING (true) on mutations)
- Never expose API keys in VITE_ environment variables, use Supabase Edge Functions for third-party APIs
- Add security headers (CSP, X-Frame-Options, HSTS, X-Content-Type-Options)
- Add rate limiting to all API endpoints and Edge Functions
- All auth checks must be enforced server-side via RLS policies, not just in UI components
Prevention is faster than remediation. But if you've already deployed without this, the checklist above will get you to a secure state.
Sources: Wiz, Common Security Risks in Vibe Coded Apps (2025) · Veracode GenAI Code Security Report (2025) · Databricks, Passing the Security Vibe Check (2025)
Frequently Asked Questions
- Is Bolt.new safe for production apps?
- Bolt.new can generate functional full-stack apps very quickly, but the default output is not production-secure. Common issues include disabled Supabase RLS, missing security headers, exposed environment variables, and no rate limiting. You need to audit and harden the generated code before deploying to production.
- Does Bolt.new expose my API keys?
- It can. Bolt.new uses Vite as its bundler, and any environment variable prefixed with VITE_ is included in the client-side JavaScript bundle. If Bolt.new puts your API keys in VITE_ variables, or hardcodes them directly, anyone visiting your site can extract them from the browser.
- How do I add security headers to a Bolt.new app?
- Bolt.new generates Vite-based apps that don't include security headers by default. You need to add them manually either in your vite.config.ts for development, or in your deployment platform's configuration (vercel.json, netlify.toml, etc.) for production. Key headers to add: Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, and Strict-Transport-Security.
Your AI writes the code. We find what it missed.
Paste your URL. Security audit in 60 seconds.
Scan my app