How to Secure a Bolt App Before Launch: The Complete Security Checklist
Bolt apps ship with Supabase RLS gaps, exposed secrets, and unauthenticated APIs. Use this complete security checklist to harden your app before real users arrive.
Knowing how to secure a Bolt app is the step that separates a working prototype from something you can safely give to real users. Bolt.new generates full-stack applications at speed—most backed by Supabase and a Node.js API layer—and the results look production-ready until you start testing authorization. The generated code functions correctly and the UI tests pass, but the same patterns that make Bolt's output fast to ship also make it fast to compromise: .env secrets exposed in shared project links, Supabase Row Level Security disabled on generated tables, and API routes that authenticate requests without checking whether the requesting user owns the data they are asking for.
The built-in Bolt security audit on paid plans catches some of this—database configuration issues, a subset of exposed secrets. It does not test authorization logic. It cannot verify that user A cannot read user B's data, that a logged-in user cannot modify another account's records by substituting IDs, or that an API route is correctly rejecting elevated-privilege requests from a standard user. These are the vulnerabilities that matter in a multi-user application, and they require manual testing to confirm.
This guide covers the specific security gaps that appear most often in Bolt-built applications: WebContainer secret exposure, Supabase RLS misconfigurations, missing server-side authorization, and IDOR vulnerabilities in generated API routes. It follows the same audit methodology we apply in our security audit service for vibe-coded applications. For comparison with another AI coding tool's security profile, the companion post on how to secure a Lovable app covers the same vulnerability classes in the Lovable-specific context.
Why Bolt Apps Ship With Security Gaps
Bolt's WebContainer runs Node.js entirely in the browser. This means the file system, environment variables, and server process all live in a sandboxed in-browser environment during development. It is a remarkable engineering achievement, but the security implication is significant: any .env file created in that WebContainer is a plain text file in that container's virtual file system. When a developer shares a project link or exports the project files, the .env file goes with it—along with any API keys, database passwords, or Supabase service_role keys stored in it. Bolt's documentation recommends moving secrets to your hosting provider's environment variable settings before deploying, but it does not enforce this, and it is a step many developers skip under deadline pressure.
- →Secrets in the WebContainer .env file: any secret stored in .env during Bolt development is visible to anyone with access to the Bolt project link. API keys, Stripe secret keys, Supabase service_role keys, and OpenAI keys are all common finds. Move every secret to the deploy target's environment variable settings and remove it from the committed .env before the first deployment.
- →Supabase service_role key in generated code: Bolt-generated Supabase integrations occasionally use the service_role key in API routes or utility files. The service_role key bypasses all Row Level Security policies on every table. It belongs exclusively in server-side code that verifies the caller's identity before executing privileged operations—never in a client-importable utility.
- →Generated API routes without authorization middleware: Bolt generates Express or Hono API routes that authenticate the caller by verifying the JWT but frequently skip the ownership check—verifying that the authenticated caller is allowed to access the specific resource by ID. A route like GET /api/documents/:id that returns the document after checking auth.uid() IS NOT NULL will return any document to any logged-in user.
- →Client-side-only input validation: generated form components validate inputs before submission, which provides a good user experience. They do not prevent a caller from bypassing the form entirely and submitting crafted payloads directly to the API. Server-side validation using Zod or Joi on every API route is the only reliable server-side defense.
- →Supabase RLS disabled on migration-created tables: the same pattern that affects Lovable apps applies to Bolt apps backed by Supabase. Generated migration files create tables but do not reliably add matching Row Level Security policies. A table with RLS disabled returns all rows to any client that has the Supabase anon key—which is always public.
Supabase RLS in Bolt Apps: The Most Dangerous Default
Most Bolt apps that use Supabase as their database backend suffer from the same RLS misconfiguration pattern found across all vibe-coded applications: tables created by generated migrations have Row Level Security disabled, or policies that check authentication without checking ownership. The difference between these two states is the difference between a database accessible to any logged-in user and one that actually enforces data isolation between accounts.
The same vulnerability category—RLS disabled on Supabase tables—is the single highest-frequency critical finding in audits of Lovable, Bolt, v0, and Cursor-generated applications. Our detailed guide on Supabase RLS security in AI-built apps covers the SQL queries to audit every table, the ownership predicate patterns that actually enforce data isolation, and how to test cross-user access manually. Run those checks on every table in your Supabase project before proceeding with the Bolt-specific review below.
- →Audit RLS on every table: in the Supabase SQL Editor, run SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; and verify every table returns rowsecurity = true. Any false is a critical finding that must be resolved before real user data enters the table.
- →Verify ownership predicates on every policy: run SELECT tablename, cmd, policyname, qual FROM pg_policies WHERE schemaname = 'public'; and check that every SELECT, INSERT, UPDATE, and DELETE policy includes auth.uid() = user_id rather than just auth.uid() IS NOT NULL.
- →Check tables created by Supabase Edge Functions: Bolt sometimes generates Edge Functions that create tables on first call. Those tables also need RLS enabled and policies defined. Check for any tables without corresponding entries in pg_policies.
- →Test with two accounts: create two test users, log in as each, and attempt to read the other user's data by submitting direct Supabase queries through the browser console. If user A can read user B's documents, your RLS policies do not enforce ownership—regardless of what the policy definitions say.
Server-Side Authorization: The Layer Bolt's Audit Misses
Bolt's built-in security audit reviews database configuration and flags obvious exposed secrets. It does not test whether your API routes correctly enforce data ownership. Authorization bugs at the API layer are distinct from database-layer RLS gaps and exist even in apps with correct Supabase policies—they appear in the server-side route logic that processes requests before they reach the database.
- →Read user identity from the verified token, not from the request body: every API route that reads or modifies user data should extract the user's ID from the verified JWT. A route that accepts userId from req.body allows any authenticated user to perform actions as any other user by substituting the target's ID in the request payload.
- →Parameterized queries with ownership filters: when a route fetches a resource by ID, include the calling user's ID in the query predicate. For Supabase queries, use .eq('user_id', authenticatedUserId).eq('id', resourceId)—both conditions must match. Without the ownership filter, the route returns data for any ID regardless of who owns it.
- →Role checks from the database, not the request: admin and billing routes must verify the user's role from the server-side session or a database lookup, never from a role claim in the request body. A route that checks req.body.role === 'admin' before performing a privileged operation can be exploited by any user who adds a role field to their request.
- →Rate limiting on authentication endpoints: sign-up, sign-in, and password reset routes should have request rate limiting to prevent brute-force attacks. Bolt does not add rate limiting by default. The express-rate-limit package (or equivalent for Hono) covers this in a few lines of middleware at the application level.
Secrets Management: Moving From WebContainer to Production
Bolt's WebContainer is a development environment, not a secrets vault. Every secret you add to the .env file during development should be treated as a temporary placeholder that must be migrated to your production environment's secrets management before the first deployment. Run DeployReady before your first deployment to catch secrets that made it into committed files or the compiled JavaScript bundle—it runs entirely locally and returns a prioritized findings list in under a minute.
- →Move all secrets to the deploy target's environment: for Vercel, use vercel env add. For Netlify, use the environment variables section in Site settings. For Fly.io, use flyctl secrets set. Confirm each secret is available as process.env.SECRET_NAME in your server code and does not appear in the Bolt .env file that gets committed.
- →Audit VITE_ prefixed environment variables: any variable prefixed VITE_ is inlined into the compiled client-side JavaScript bundle by Vite. This prefix is appropriate only for non-secret values like public API URLs. A VITE_STRIPE_SECRET_KEY or VITE_SUPABASE_SERVICE_ROLE_KEY is visible to every user who opens your app's JavaScript bundle in DevTools.
- →Rotate any secret that appeared in the WebContainer .env: if a secret was in the .env file during development and you shared the project link or exported the project files, treat that secret as compromised. Rotate it in the originating service—Stripe, OpenAI, Supabase—before deploying the app with the new key.
- →Verify .env is in .gitignore: Bolt projects use Vite, which includes .env in .gitignore by default, but verify this before running git add. A .env file that reaches version control exposes secrets to everyone with repository access and, for public repos, to the entire internet.
CORS and Security Headers in Bolt Deployments
Bolt-generated Express or Hono servers frequently configure CORS with a wildcard origin—Access-Control-Allow-Origin: *—which allows any website to make cross-origin requests to your API. In a JWT-authenticated app, CORS is less critical for direct data protection than RLS, but it is required by most enterprise security questionnaires and is a meaningful defense-in-depth control. Security headers are a separate concern: they are set at the response level and protect against XSS, clickjacking, and MIME-sniffing attacks regardless of your authentication model.
- →Restrict CORS to your production origin: replace origin: '*' with origin: 'https://yourapp.com' in your CORS configuration. For apps with multiple origins, use an explicit allowlist with a validation function rather than a wildcard.
- →Configure security headers at the hosting layer: Vercel, Netlify, and Cloudflare all support response header configuration in vercel.json, netlify.toml, or a Worker. The minimum set for any production SaaS: Strict-Transport-Security (HSTS with includeSubDomains), X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, and a Content-Security-Policy that restricts script and frame sources.
- →Disable source maps in the production build: source maps expose your application's unminified source code in the browser DevTools, giving any visitor a readable copy of your server logic and business rules. Set sourcemap: false in your Vite production config. Source maps are invaluable for debugging locally; they are a reconnaissance tool in production.
The Complete Bolt App Security Checklist Before Going Live
Before you share your Bolt app URL with real users, work through each item below. Every item maps to a vulnerability class found in Bolt app security reviews. Run DeployReady first to establish your automated baseline—it catches exposed secrets, missing security headers, and environment configuration issues in a single command—then use the checklist below for the manual authorization review.
- →Secrets not in .env: every secret migrated to hosting platform environment variables. No VITE_ prefix on any private key, password, or third-party credential.
- →No service_role key in client-accessible code: search the project for service_role. Rotate immediately if found in a client bundle or utility file imported by client components.
- →Supabase RLS enabled on every table: SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; returns true for every row.
- →RLS policies enforce ownership: every SELECT, INSERT, UPDATE, DELETE policy includes auth.uid() = user_id. No policy uses USING (true) or auth.uid() IS NOT NULL as its only condition.
- →API routes read user ID from the verified token: no route accepts userId from the request body for authorization decisions.
- →Resource fetches include an ownership filter: every query by resource ID includes .eq('user_id', authenticatedUserId) or equivalent Prisma where clause.
- →CORS restricted to production origin: no wildcard origin in server configuration.
- →Security headers configured: HSTS, X-Content-Type-Options, X-Frame-Options, and CSP present on all responses.
- →Source maps disabled in the Vite production config: sourcemap: false in vite.config.ts for the production build.
- →Server-side input validation on all API routes: Zod or equivalent schema validation on request bodies before processing.
- →Rate limiting on auth endpoints: sign-up, sign-in, and password reset routes protected against brute force.
- →Two-account IDOR test passed: user A cannot read, modify, or delete user B's resources through direct API calls or Supabase SDK queries.
Frequently Asked Questions
Is Bolt.new safe for building production applications?
Bolt generates functional production applications but not inherently secure ones. The WebContainer environment optimizes for development speed, and the AI-generated code prioritizes working functionality over secure defaults. A Bolt-built app is safe for production after a manual security review covering RLS policies, server-side authorization on every API route, secrets migration to the hosting platform, and CORS and security header configuration. The built-in Bolt audit on paid plans is a useful starting point but does not substitute for manual authorization testing across real user accounts.
What are the most common security vulnerabilities in Bolt apps?
The highest-frequency findings in Bolt app security reviews are: Supabase RLS disabled or misconfigured on generated tables (allowing any logged-in user to read any other user's data), API routes that authenticate callers without checking data ownership (IDOR vulnerabilities), and secrets—especially Supabase service_role keys and third-party API credentials—left in the WebContainer .env file and deployed to production or committed to version control. Missing security headers and wildcard CORS configuration are near-universal findings but are lower severity.
Does Bolt's built-in security audit make my app secure?
Bolt's built-in security audit on paid plans covers database configuration issues and some exposed secrets patterns. It does not test authorization logic—specifically, whether your API routes and RLS policies prevent user A from accessing user B's data. Authorization bugs are the highest-risk vulnerability class in multi-user applications, and they require manual testing with real user accounts to detect. Run the audit as a first pass, then conduct manual authorization testing against every resource type before onboarding real users.
How do I test for IDOR vulnerabilities in a Bolt app?
Create two test accounts (user A and user B). Log in as user A, create a resource, and record its ID. Log in as user B and attempt to fetch that resource by ID using the Supabase client SDK in the browser console: supabase.from('your_table').select('*').eq('id', resourceIdFromUserA). If the query returns data, your RLS policy does not enforce ownership. Repeat for every resource type in your app, and also test POST, PATCH, and DELETE operations using user B's session with user A's resource IDs. Any successful cross-user operation is an IDOR vulnerability.
Can I use DeployReady to check a Bolt app?
Yes. DeployReady runs static security checks on any web application regardless of how it was built. Run npx deployready in your project directory to get a 0-100 readiness score and a prioritized list of findings covering exposed secrets, missing security headers, environment configuration issues, and OWASP Top 10 patterns. It runs entirely locally with no data sent to external servers, takes under a minute on a typical Bolt project, and is free and MIT-licensed.
How Belsoft Helps Secure Bolt Apps
Belsoft runs focused security audits for teams that built with Bolt and need to ship to real users, onboard enterprise customers, or respond to a vendor security questionnaire. The audit covers the full Bolt security surface: every Supabase table reviewed for RLS configuration and tested for cross-user access, every API route checked for authorization gaps and IDOR vulnerabilities, the deployed bundle inspected for exposed secrets, CORS and security header configuration reviewed, and authentication settings audited end-to-end. We start with DeployReady to establish the automated baseline in minutes, then go further with manual testing that exercises the actual authorization logic under real attack conditions—because that is where Bolt's built-in audit stops and where the real findings are.
The output is a ranked findings report with specific code changes and SQL policies for each finding—ready to deploy, not just described—a retest to confirm fixes before you share results with customers or a security questionnaire, and documentation your team uses to maintain the security posture as the app evolves. If you are preparing for your first enterprise deal, responding to a vendor security review, or you shipped fast and want to know what is actually exposed, this is the right starting point. Book a call to discuss your Bolt app.
“A Bolt app with unprotected API routes and disabled RLS is not a fast-moving startup—it is a database open to every logged-in user.”
Written by
Marco Silva
Security Engineer, Belsoft Solutions
More from the blog
Ready to partner?
Let's talk about your company.
30 minutes. No pitch. We talk through how you run today and where AI and automation would help.
Book a free audit
