Is my Bolt.new app secure? What the audit can miss
Bolt's security audit and database check catch a lot. Six Bolt.new security checks for what they can miss: RLS, admin flags, storage, keys and Stripe.
You built an app in Bolt, it works, and the Publish button is right there. Bolt now has its own security checks, so it is fair to ask: do I still need to worry?
Short answer: use Bolt's checks, they are good. But they fix what they recognise, and a few common mistakes look perfectly normal from the inside. This guide covers what Bolt checks, where the gaps are, and six tests you can run yourself in about 20 minutes.
What Bolt already checks for you
Bolt has two separate security checks, and it helps to know which one you have.
The database Security tab (all plans). Click the database icon at the top of your project, then Security. According to Bolt's documentation, this page runs the same checks as the Security Advisor in Supabase. Bolt Database is built on Supabase technology, so the same rules apply. It flags things like a table with Row Level Security (RLS) switched off, a policy with a condition that is always true, a function with a mutable search path, and leaked password protection being off. There is an Ask Bolt to fix button for each problem.
The project security audit (paid plans only). In the Publish menu, click Run security audit. Bolt's help page says it reviews your code and database for who can see and change data, what your app reveals publicly, sign-in and sessions, ways the app can be misused (like changing a price or claiming a single-use item twice), unsafe input, and keys in your code. It applies fixes itself and lists anything that needs your action. It doesn't use tokens, and you can run it up to 30 times a day.
That is a solid head start. On a paid plan, run the audit before every publish. On the free plan you only get the database check, so the tests below matter even more.
Where the gaps are
Automated checks look for patterns. The database check flags a policy written as USING (true), but not one that looks restrictive and isn't. And neither check sees your live site the way a stranger does. The six tests below cover those gaps.
1. Find "any signed-in user can read everything"
This is one of the most common real leaks in Supabase-backed apps, and easy to miss because RLS is on.
The pattern looks like this:
create policy "Authenticated users can read orders"
on public.orders for select
to authenticated
using (true);
or the same policy with using (auth.uid() is not null). Both mean anyone with an account can read every row, and on most apps anyone can create an account in ten seconds. The first version gets an "always true" warning on the Security tab, which is easy to dismiss because "only signed-in users" sounds safe. The second version isn't an always-true pattern, so you may get no warning at all.
Bolt's docs describe the visitor version: without sign-in, Bolt may write policies that let any visitor view, change and delete data. Adding sign-in only helps if the policies then check whose row it is.
How to check: in Bolt, open the database view and look at the policies on each table that holds personal data (orders, messages, profiles, invoices, bookings). Or paste this into the Bolt chat:
List every RLS policy in my database in a table: table name, command (select/insert/update/delete), role, and the exact USING and WITH CHECK conditions. Flag any policy on a table with user data where the condition does not compare a column to auth.uid().
A safe policy for private data compares the row's owner to the signed-in user:
using (auth.uid() = user_id)
If RLS itself is new to you, Supabase Row Level Security in plain English explains it in five minutes.
2. Try to make yourself an admin
Many apps store a role in a profiles table: role = 'user' or role = 'admin'. AI tools usually add a policy so users can update their own profile, which sounds right. The problem is that the policy often allows updating every column of their own row, including role, is_admin, plan or credits.
So a user can open the browser console and send their own update request with role: 'admin', without ever touching your app's buttons.
How to check: ask Bolt directly:
Can a signed-in user change their own role, is_admin, plan, credits or subscription status by sending an update request straight to the database with the public key? Check the RLS policies, column privileges and triggers on every table that has such a column, and show me the exact SQL that prevents it.
Good fixes: keep roles and plans in a table users can't write to, revoke update on those columns from the authenticated role, or add a trigger that rejects changes. Hiding the field on screen is not a fix.
One related trap: a policy that reads the role from user_metadata in the login token. Supabase warns that users can change their own user_metadata (advisor 0015). The database check flags this one as an error, so if you see it, fix it first.
3. Do the two-account test
This simple test catches problems in both policies and server functions.
- Open your live app in a normal window and sign up as user A. Create something private: an order, a note, a message.
- Open a private window and sign up as user B.
- As user B, look at every list page. Do you see anything of A's?
- Copy the address of A's private item (for example
/orders/123) and open it as B. - As B, open the browser's developer tools (F12), go to Network, and reload a page that loads data. Click a request to your database (its address usually contains
/rest/v1/). Look at the Response. Is it only B's data, or more?
If B sees anything of A's, that table's policy is wrong. Paste what you saw into Bolt, ask it to fix the policy, and repeat.
4. Check your storage buckets
If your app has uploads (avatars, receipts, ID photos, PDFs), Bolt keeps them in storage buckets. Bolt's file storage page labels each bucket Public or Private: public means anyone with the link can open the file, even when signed out. Bolt says it is designed to create private buckets for user uploads, and also tells you to confirm the visibility yourself.
How to check: database icon, then File Storage. For each bucket, ask: would I be fine if a stranger saw these files? Avatars and product images can be public. Anything personal should be private, with policies so each user can only reach their own folder, and signed URLs instead of public links.
Also watch for public buckets that strangers can list file by file. Our guide to checking Supabase storage buckets shows how to test both.
5. Make sure secret keys never reach the browser
Bolt web projects are usually Vite apps. Every variable that starts with VITE_ gets copied into the JavaScript that every visitor downloads. That is fine for the database address and the public (anon) key, which are designed to be public. It is not fine for an OpenAI key, a Stripe secret key, a Resend key or the database's service key, which skips every RLS policy.
Bolt has a Secrets tab (database icon, then Secrets) for exactly this. Secrets there are read by server functions and, per Bolt's docs, never end up in your users' browsers. Problems start when a key gets pasted into a .env file "just to make it work".
How to check:
- Search your project files for
VITE_and read every name. Anything that sounds secret (SECRET,SERVICE,PRIVATE,OPENAI,STRIPE_SECRET) is in the wrong place. - On the live site, open developer tools, go to Sources, open the main JavaScript file and search for
sk-andsk_live. You should find nothing.
If you find a secret key, move it to the Secrets tab, have Bolt call it from a server function, and then create a new key at the provider. The old one was public and should be treated as stolen. Your API key is probably sitting in your JavaScript walks through this in more detail.
6. Check server functions and payments
Server functions (Bolt also calls them edge functions) do the sensitive work: AI calls, emails, payments. A deployed function lives at a public address. The question is whether it checks who is calling and whether that person is allowed to do this.
Two things to check:
- Login checks. A function that sends email, calls a paid AI model or changes data should reject callers who aren't signed in, and should check the user is allowed to act on that specific record. A common quick fix for a "401 Unauthorized" error during building is to switch the login check off. Our guide on Supabase Edge Functions open to anyone shows how to tell, and the same idea applies here.
- Prices and payment status. Bolt's Stripe integration creates the checkout and webhook functions for you and verifies webhook signatures automatically, which removes the worst risk. What still matters is your own logic around it: the price should come from Stripe or your server, never from the browser, and "this user has paid" should only be set by the webhook, never by a column the user can update (see test 2). Use Stripe test mode and test cards before going live, as Bolt recommends.
Two settings worth a look
- Leaked password protection. It is on by default for Bolt Database. If you claimed your database into your own Supabase account or connected Supabase on the free Supabase plan, Bolt notes it is turned off and not available on that plan.
- Site URL and redirect list. In the authentication settings, the Site URL should be your live address, not
localhost:3000. Keep the redirect allow list to your own domains, with wildcards only where you need them.
The 20-minute version
- Run the database Security tab, and the full security audit if you are on a paid plan. Fix every error.
- Read the policies on tables with personal data. Each should compare a column to
auth.uid(). - Ask Bolt whether users can change their own role, plan or credits.
- Do the two-account test on the live site.
- Make every bucket with personal files private.
- Search for
VITE_secrets andsk-in the published JavaScript, and rotate anything you find. - Check that server functions require a signed-in user and that payment status only comes from the webhook.
For the basics of moving keys server-side and adding headers, our shorter guide on how to secure your Bolt app has copy-paste prompts.
Check it from the outside
Bolt's tools look at your project from the inside. Greenlight Scan looks at your published app the way a stranger would: it reads the JavaScript your site ships, looks for secret keys, checks for exposed files and headers, and, once you verify you own the site, tests whether your database tables answer to the public key. It only counts rows and never downloads your data. Run the free scan to get your traffic-light result in about a minute. The $19 Full Scan unlocks every finding with the evidence and a fix prompt written for Bolt, plus re-scans for 30 days.
If your app takes payments or stores personal data, some risks above live in code an outside scan can't see, like a server function that never checks who owns a record. The $79 Launch Audit adds a deep AI review of the code in your GitHub repo, every finding double-checked by a second AI, and a launch verdict. It is automated, not a pentest.
The Friday leak report
One short email a week: the newest ways AI-built apps leak data, and the one-line fix for each. No spam, unsubscribe any time.