Home / Guides / Bolt.new Security
Security guide · 2026

Bolt.new security: what to audit before you launch

Bolt.new can take you from idea to deployed app in under an hour. That speed is real. So is the gap between "it works" and "it's safe to put real users on." This guide covers the specific vulnerabilities Bolt-generated apps ship with, with exact file paths and fixes.

Security Bolt.new Supabase ~10 min read

The core problem with Bolt.new apps

Bolt.new generates fully functional React, Next.js, and Remix applications with a Supabase backend, third-party integrations, and deployment targets like Netlify and Vercel. The code it produces is reasonable quality by the standards of what it's optimizing for: shipping something you can demo quickly.

The problem is that "good enough to demo" and "safe to launch with real users and real data" have different checklists. Bolt's generation defaults are tuned for the former. You need to complete the latter yourself before you go live — and the gaps are consistent enough across Bolt-generated apps that we can tell you exactly where to look.

We reviewed over 40 Bolt.new-generated codebases in 2025–2026. Four categories of issues appeared in the large majority of them. They are not hard to fix. But they are non-obvious if you didn't write the code yourself, which is exactly the situation most Bolt users are in.

1. Supabase RLS is disabled by default

This is the most critical issue, and it is present in nearly every Bolt.new app that uses Supabase. Row-level security (RLS) is Supabase's mechanism for restricting which rows a user can read or write. When it is disabled, the Supabase REST API will return every row in a table to any caller who presents the anon key — which is visible in plain text in your browser's network tab.

The consequences are direct: user records, email addresses, payment data, and application state are readable by anyone who knows your project URL. The Supabase anon key is designed to be public, but it provides no protection without RLS policies in place.

Bolt does create Supabase migrations, but it does not enable RLS or add owner-scoped policies unless you explicitly prompt it to. The default generated schema has RLS disabled on every table.

P0 · Critical Row-level security disabled on all tables supabase/migrations/0001_initial.sql
Finding

Supabase migrations created by Bolt.new do not include ALTER TABLE … ENABLE ROW LEVEL SECURITY statements or any RLS policies. Every table in the database is fully accessible to any caller presenting the public anon key, without any row-level access control.

Recommended fix

Add RLS-enabling statements and owner-scoped policies to your migration file. For a profiles table where each row belongs to the authenticated user:

-- Enable RLS on the table
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;

-- Policy: users can only select their own row
CREATE POLICY "select_own_profile"
  ON profiles FOR SELECT
  USING (auth.uid() = user_id);

-- Policy: users can only update their own row
CREATE POLICY "update_own_profile"
  ON profiles FOR UPDATE
  USING (auth.uid() = user_id)
  WITH CHECK (auth.uid() = user_id);

Apply the same pattern to every table in your schema. Verify in the Supabase dashboard: Table Editor → select table → RLS should show "Enabled" with at least one policy listed.

Quick test: open your deployed app in Chrome, open DevTools → Network, trigger any data load, find a request to your-project.supabase.co/rest/v1/, copy the URL and the apikey header value, then run curl 'https://your-project.supabase.co/rest/v1/your_table?select=*' -H 'apikey: YOUR_ANON_KEY'. If you get rows back without being authenticated, RLS is disabled.

2. Environment variables end up in the client bundle

Bolt.new apps are typically built on Vite or Next.js. Both frameworks have a convention for distinguishing server-side secrets from client-side values: in Vite, any variable prefixed with VITE_ is bundled into the browser JavaScript; in Next.js, any variable prefixed with NEXT_PUBLIC_ is similarly exposed.

Bolt frequently generates code that puts sensitive API keys — OpenAI, Stripe publishable keys, Resend API keys, and others — into variables with the public prefix, or directly references them in client components. The intent is to make the call work in the browser. The effect is to publish your API key to every visitor who opens DevTools.

We have seen Bolt-generated apps with VITE_OPENAI_API_KEY, VITE_STRIPE_SECRET_KEY, and VITE_RESEND_API_KEY all present in the client bundle. Any of these allow an attacker to make API calls billed to your account.

P0 · Critical Secret API key exposed in client-side bundle src/lib/openai.ts · line 3
Finding

The OpenAI client is instantiated in a file imported by client components, using import.meta.env.VITE_OPENAI_API_KEY. The VITE_ prefix causes Vite to inline this value into the production JavaScript bundle, making it visible to any visitor in the browser's Sources tab or by fetching the JS asset directly.

// src/lib/openai.ts — as generated by Bolt.new
import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: import.meta.env.VITE_OPENAI_API_KEY, // ← bundled into browser JS
  dangerouslyAllowBrowser: true,               // ← explicitly bypasses the SDK warning
});
Recommended fix

Move all calls to secret APIs to a server-side route. In Next.js, create an API route at app/api/generate/route.ts. In a Vite SPA, deploy a Supabase Edge Function or a separate serverless function, and call that from the browser instead. The browser should never hold a secret key.

// app/api/generate/route.ts (Next.js App Router)
import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY, // server-only, no NEXT_PUBLIC_ prefix
});

export async function POST(req: Request) {
  const { prompt } = await req.json();
  const completion = await openai.chat.completions.create({ ... });
  return Response.json({ result: completion.choices[0].message.content });
}

3. No error tracking is wired in

Bolt.new apps ship without error tracking. There is no Sentry, no Datadog, no BugSnag — nothing that will alert you when a runtime error occurs in production. When a user hits a crash, a failed payment, or a broken API call, you find out from a support email, a Stripe dispute, or a churn. You have no idea how often it happened before that point.

This is not a security vulnerability in the traditional sense, but it is a monitoring gap with security consequences: without error tracking, you have no visibility into repeated failed auth attempts, API errors that may indicate scraping or abuse, or exceptions thrown by your payment logic. Blind spots in monitoring are how breaches go undetected.

The fix is straightforward. Sentry's free tier covers most early-stage apps. The installation for a Vite or Next.js app is under 10 minutes.

Minimum viable monitoring setup: install Sentry (or Highlight.io for a self-hostable alternative), set SENTRY_DSN as a server-side environment variable, instrument your payment and auth routes with custom error captures, and set an alert rule for any new error type seen more than three times in one hour. That single rule catches a large fraction of production incidents before users report them.

4. Auth route protection gaps

Bolt.new generates authentication using Supabase Auth, which is solid. The gap is in how the generated React router or Next.js routing handles protected pages. Bolt frequently creates client-side route guards — a check on the client that redirects unauthenticated users — without any server-side enforcement.

Client-side guards are cosmetic protection. A user who bypasses JavaScript (or who uses the API directly) can access the protected route. In Next.js apps, this means no middleware enforcing authentication at the edge. In React Router apps, it means the data load for a protected page may execute before the auth check completes.

For Next.js apps, the fix is a middleware.ts at the project root that checks the Supabase session cookie and redirects to login for unauthenticated requests to protected paths. For React Router apps, use a loader that throws a redirect on the server if the session is absent.

P1 · High Protected routes enforced client-side only src/components/ProtectedRoute.tsx · line 1
Finding

Route protection is implemented as a client-side React component that checks supabase.auth.getUser() and renders null or a redirect while the auth state resolves. This means the protected page component and its data fetching begin mounting before authentication is confirmed, and any user who disables JavaScript or intercepts API calls can access protected endpoints directly.

// src/components/ProtectedRoute.tsx — as generated by Bolt.new
export function ProtectedRoute({ children }) {
  const { user, loading } = useAuth();
  if (loading) return null;          // data may already be fetching
  if (!user) return <Navigate to="/login" />;
  return children;
}
Recommended fix (Next.js)

Add middleware.ts at the project root using the Supabase SSR client:

// middleware.ts
import { createServerClient } from '@supabase/ssr';
import { NextResponse } from 'next/server';

export async function middleware(request) {
  const response = NextResponse.next();
  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
    { cookies: { ... } }
  );
  const { data: { user } } = await supabase.auth.getUser();
  if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  return response;
}
export const config = { matcher: ['/dashboard/:path*', '/settings/:path*'] };

Pre-launch security checklist for Bolt.new apps

  1. Enable RLS on every Supabase table. Check the Supabase dashboard: Table Editor → each table → RLS tab. If it says "Disabled," stop here and add policies before you continue.
  2. Audit your environment variables. Search your codebase for VITE_ and NEXT_PUBLIC_. Every variable with these prefixes is visible in the browser. Move anything sensitive — API keys, secrets, service role keys — to unprefixed variables used only in server-side code.
  3. Rotate any key that was ever in the client bundle. Even if you move it server-side now, if it was ever deployed to a VITE_ variable, treat it as compromised and rotate it.
  4. Add server-side auth middleware. Client-side route guards are not sufficient. Add middleware.ts (Next.js) or server-side loaders (Remix) that check session server-side before returning protected data.
  5. Install error tracking. Sentry, Highlight.io, or equivalent. Configure alerts on error rate spikes, especially for auth and payment flows.
  6. Verify HTTPS on your custom domain. If you connected a custom domain to Netlify or Vercel, confirm HTTPS is enforced with an auto-redirect from HTTP, and that HSTS is set in your response headers.
  7. Add rate limiting to auth endpoints. Supabase supports rate limiting at the project level. Enable it in the Auth settings, and consider an edge middleware layer for additional protection on login and signup endpoints.
  8. Check your Supabase storage buckets. If you use Supabase Storage, verify that buckets intended to be private are not set to public. Public buckets serve any file without authentication.

What this guide does not cover

This guide covers the security gaps specific to Bolt.new's generation defaults. It does not cover application-specific business logic vulnerabilities (authorization flaws in your specific domain), OWASP Top 10 issues that depend on what your app does, or performance and reliability concerns. For a full picture — including OWASP coverage, dependency vulnerabilities, N+1 query detection, and monitoring gap analysis — run a Launch Readiness Audit.

How to check your Bolt.new app for free

Start with our free tools: the Security Headers Analyzer will check HTTPS enforcement and header configuration, and the Email Security Checker will verify SPF and DMARC on your domain. For RLS exposure, secrets in client bundles, and the full set of issues above, run a free Launch Readiness Score. No code access required — we scan the live app from the outside in about 60 seconds, then tell you exactly what to fix.

Is your Bolt.new app ready to launch?

Paste your URL for a free Launch Readiness Score across security, reliability, performance, and monitoring. Takes about 60 seconds. No code access needed.

Scan my app — free

This guide covers security patterns observed in Bolt.new-generated applications. Specific behavior may vary with Bolt.new product updates. Last reviewed June 2026. This is general information, not a security guarantee. For production systems handling regulated data, engage a qualified security assessor. Contact: info@launchreadycode.com · launchreadycode.com