Skip to content
LockShip

Guide · Bolt · Updated

Is my Bolt app secure? A plain check before you launch

You typed an idea into Bolt, and a few evenings later there is a real app with a real address. Maybe friends are already signing up, and a small question keeps coming back: if a stranger went looking, what would they find? This guide is our honest attempt to answer that, in plain words, with your app open in the next tab. Nobody can promise you an app is secure, us included. What you can do is look in the handful of places where problems usually hide.

The honest short answer

Bolt starts you off with sensible defaults. Its help center says Bolt turns on row-level security for the tables it creates, that it is designed to create private storage buckets with access rules when you store user uploads, and that leaked-password protection is on by default when it creates your database. Paid plans also get a full security audit from the Publish menu that reviews your code and database and fixes what it can, without spending your tokens.

The gaps are in the parts only you can judge. Bolt’s own database security page puts it more directly than we would have dared: “Anyone who visits your site can send requests to your database directly, without using your site’s screens.” The same page warns that if you haven’t asked Bolt to add sign-in, it “might write policies that allow any visitor to view, add, change, and delete the data in your database.” That is fine for a public menu. It is not fine for your customers’ orders.

So the real question is not “is Bolt secure?” but “does my app only show strangers what I meant to show them?” You can check that, and most of it takes minutes.

What your Bolt app is made of

  • Hosting. By default, Bolt Cloud publishes your site to a free bolt.host address, or to your own domain on a paid plan. Some projects publish to Netlify instead, at a netlify.app address.
  • Database. New projects use Bolt Database. On Pro and Teams plans you can connect your own Supabase project instead. The two are closer than they sound: Bolt’s email setup page says Bolt-managed databases run on shared Supabase infrastructure. The checks apply to both and show where to click in each.
  • Server functions. When something shouldn’t run in a visitor’s browser, like taking a payment or calling an AI model, Bolt writes a server function (also called an edge function) and keeps the keys it needs in Secrets.

A stranger sees everything your page downloads: the HTML, the JavaScript, any key inside it, and the addresses of your database and functions. From there they can talk to your database and functions directly, just as your app does. That is normal for this kind of app, and it is why the rules behind those addresses matter so much.

You won’t find a scary percentage here. When the security firm Escape studied thousands of public vibe-coded apps, its sample included only a small number of Bolt.new apps, so its numbers say little about Bolt in particular. Your own app is what counts.

The eight checks

Do them in order; the first might do much of the work for you. Only test an app you own, and note anything you change.

  1. Run Bolt’s own security checks first
  2. Decide who each table is for
  3. Ask your database for data as a stranger
  4. See which keys your page carries
  5. Make your server functions ask who is calling
  6. Look at your file storage
  7. Check who can see the project, not just the site
  8. Read your headers and look for source maps

01

Run Bolt’s own security checks first

Bolt has two checks worth running before anything else.

  • The project security audit (paid plans). Click Publish, then Run security audit. Bolt’s help page says it reviews who can see and change your data, what your app makes public, how people sign in, ways the app can be misused, the input it accepts, and whether private keys sit in your code or get sent to browsers. It fixes most problems on its own and lists anything it can’t fix under a “Needs your action” heading. Use the button rather than asking in chat: the button doesn’t cost tokens, and a prompt does.
  • The database security check (all plans). Click the database icon at the top of your project, then Security. Bolt says this page uses the same checks as Supabase’s Security Advisor and lists the most serious issues first. If a warning’s name means nothing to you, the Supabase advisors docs explain each one.

Bolt’s July 2026 announcement recommends running the audit on every publish, “because the risky change is usually the one you made at 2am.” We’d add one habit: read the summary of what it changed. If a fix breaks something, version history can take you back, but that undoes the security fix too, so describing the problem in chat is usually better.

Two warnings deserve a moment. RLS Policy Always True means a rule lets anyone through. Bolt flags it on every project, even where public access is intended, so don’t panic and don’t dismiss it. That is check 2. Leaked Password Protection Disabled means people can sign up with a password already seen in a data breach. It is on by default for Bolt databases, but Bolt’s authentication page says it is off, and unavailable, if you connected or claimed a Supabase project on Supabase’s free plan.

02

Decide who each table is for

This check needs no tools, and it is the one we would least like you to skip. A scan can tell you a rule exists. It can’t know that your orders table should only ever show customers their own orders. Only you know that.

Open your list of tables (database icon, then Tables, or Table Editor in the Supabase dashboard) and, for each one, pick one of the three answers Bolt’s database security page suggests:

  • Visitors can only view it. A portfolio, a menu, blog posts. Anyone may read, nobody outside may add, change or delete.
  • Visitors can submit to it. Contact forms, waitlists, reviews. Anyone may add a row, but nobody may read what other people sent.
  • It has to stay private. Orders, messages, profiles, anything with an email address in it. Bolt is clear that rules can only limit data to specific people when your site knows who each visitor is, so this needs sign-in.

Then ask Bolt to make the rules match. These two prompts come straight from Bolt’s docs, and you can adapt the table names:

Prompt for Bolt
Check that row-level security is on for every table in my database, and that each policy allows only the access my site needs.
Prompt for Bolt
Add email sign-in to my site. Then update the policies for the orders table so only signed-in people can view, add, change, or delete their orders.

If you add sign-in, Bolt’s authentication page notes that projects start with localhost:3000 as the Site URL. Bolt usually updates it when you publish, but if reset or confirmation links send people somewhere odd, look there first.

03

Ask your database for data as a stranger

Now test what you decided. Open your live app in Chrome, open DevTools, and go to the Network panel. Reload and click around so the app loads some data, then type rest/v1 in the filter box. Each request is your app asking the database for something. Click one: the address starts with your project URL, and under Headers there is a request header called apikey. That is your public key.

Now ask for one row the way any visitor could, from a terminal:

Terminal
curl -s "https://YOUR-PROJECT.supabase.co/rest/v1/YOUR_TABLE?select=*&limit=1" \
  -H "apikey: YOUR_PUBLIC_KEY"

Try it for every table you marked private in check 2. Real rows from a private table mean anyone on the internet can read it right now, and that is the thing to fix first. An empty list, [], means the rules filtered everything out or the table is empty. An error about permissions is also fine, if that is what you intended.

This only tests reading, on purpose. Don’t test writes by changing or deleting things on your live app. Read the rules instead. Supabase’s row level security docs say a table without RLS “is readable and writable by any role with a grant on it,” and that a rule saying using (true) for anonymous visitors hands every row to every stranger. Look for those, and check the rules for adding, changing and deleting too.

On Bolt Database

The Security tab from check 1 is your main view. Bolt’s advanced settings page says that if you need SQL editing or outside tools, you can claim the database into your own Supabase account (Pro and Teams plans).

On your own Supabase

Our Supabase RLS checklist has SQL you can paste into the SQL Editor to list every table, whether RLS is on, and what a signed-out visitor is allowed to do.

Last, the test no scanner can do for you. Make two accounts. Signed in as the second, try to open something that belongs to the first, for example by putting its order ID into the address bar. You should see nothing of theirs. This catches a rule that is switched on but too loose.

If this pattern sounds familiar, it is the same one behind CVE-2025-48757, which was about Lovable apps. Different builder, same database, same lesson.

04

See which keys your page carries

Your page should carry one key: the public one. Supabase’s API keys guide calls the publishable key (it starts with sb_publishable_) safe to expose in a web page, because it only reaches what row level security allows. Older projects use the anon key, a long string starting with eyJ, for the same job. The secret key (sb_secret_) and the older service_role key bypass row level security completely and must never reach a browser.

Look at the apikey header from check 3. If it starts with sb_publishable_, good. If it starts with eyJ, decode it in the Console tab of the same DevTools window to see which role it carries:

Browser console
// Paste into the Console of your own browser. The key never leaves your machine.
const key = "PASTE_THE_KEY_HERE";
JSON.parse(atob(key.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"))).role;
// "anon" is the public key. "service_role" means stop and read on.

Then look for anything that shouldn’t be there. Bolt’s introduction to databases names the usual settings VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY, and that prefix matters. Vite’s docs say anything starting with VITE_ is bundled into the code visitors download and should not hold secrets like API keys. Supabase says of its server-only keys: “Never prefix these, or your bundler will ship the key.” So VITE_SUPABASE_ANON_KEY is fine, while VITE_OPENAI_API_KEY would be public the moment you publish.

To search your live site, press Ctrl+Shift+F in DevTools (Cmd+Option+F on a Mac) to open the Search panel, which looks through every file the page has loaded. Try each of these, plus the name of every service you gave a key to:

Search terms
sb_secret_
service_role
sk_live_
sk-proj-
sk-ant-
secret

Code that only loads on certain screens won’t show up until you visit them, so click through first. For a thorough look, download your project from Bolt and search the built files on your computer. We ran these commands on a Vite test app with fake keys: the last line found the fake Stripe secret we had prefixed with VITE_, and the unprefixed one never reached the build.

Terminal
# Inside the project folder you downloaded from Bolt
grep -n "^VITE_" .env
npm install
npm run build
grep -rnoE "sb_secret_[A-Za-z0-9_-]+|sk_live_[A-Za-z0-9]+|sk-[A-Za-z0-9_-]{20,}|service_role" dist

Vite builds into a folder called dist; if yours differs, use that name. Treat any real-looking key as public already. Move the code that needs it into a server function, keep the key in Bolt’s Secrets so, in Bolt’s words, “private information never ends up in your users’ browsers,” then revoke the old key and make a new one. Our guide to leaked API keys in your frontend bundle covers the order to do this in, plus a script that checks every file your live site serves.

05

Make your server functions ask who is calling

Server functions do the work that needs a secret: payments, emails, AI calls. Being on a server makes them safer, not private. Anyone can call a function’s address directly, so the question for each one is what happens when a stranger does.

Bolt’s troubleshooting guide says server functions expect authenticated requests by default. There is a catch. Supabase’s docs on authorization headers explain that this platform check also lets through a caller who only sends your public key, and say plainly that “the check alone doesn’t authenticate a caller that sends only an API key.” Your public key is in your page, so the default check keeps out people who don’t look, not people who do.

That matters most for functions that cost you money or touch other people’s data. A chatbot function that forwards whatever it receives to OpenAI would happily spend your credits for a stranger. A function that reads from your database with the admin client is worse: Supabase’s guide to securing functions warns that one which doesn’t filter by the caller’s ID “returns every user’s rows.”

To test one as a stranger, find its address in the Network panel (filter for functions/v1) and call it with only the public key:

Terminal
curl -i -X POST "https://YOUR-PROJECT.supabase.co/functions/v1/YOUR-FUNCTION" \
  -H "apikey: YOUR_PUBLIC_KEY" \
  -H "Authorization: Bearer YOUR_PUBLIC_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

This really runs the function, so pick one that is cheap to run, or use a copy of your project. A function meant for signed-in users should refuse with a 401 or similar. If it sends an email, returns data, or answers like your chatbot, ask Bolt to make it check that the caller is signed in and allowed to do this, then test again.

One exception is expected. Webhooks from services like Stripe can’t send a sign-in token, so Bolt’s troubleshooting guide tells you to turn off that check for the webhook function and “add your own checks inside the server function”, such as verifying the provider’s signature. If you used Bolt’s built-in Stripe integration, Bolt says it handles webhook signature verification for you. If you wired up a webhook some other way, ask Bolt in Plan mode whether it verifies the signature.

06

Look at your file storage

Uploaded files, like avatars, invoices, or ID scans, live in storage buckets, and every bucket is either public or private. Bolt’s file storage page puts it simply: files in a public bucket can be seen by “anyone with the link, even if they’re not signed in to your app.” Supabase’s bucket docs add that a public bucket skips access control for downloading files altogether, while uploads, deletes and moves are still checked against your rules.

On Bolt Database

Database icon, then File Storage. Each bucket is labelled Public or Private. Bolt doesn’t yet let you browse individual files there, so the label is what you are checking.

On your own Supabase

Storage in the Supabase dashboard lists each bucket. The storage step of our RLS checklist has a query for buckets and a rule that keeps each user in their own folder.

Public is right for product photos and logos, and wrong for anything a customer would hate to see on a stranger’s screen. Bolt says it is designed to make private buckets for user uploads, and also to “always confirm a bucket’s visibility yourself.” If a bucket of private files is public, Bolt’s docs suggest asking it to keep the bucket private, let each user see only their own files, and use signed links instead of public ones.

07

Check who can see the project, not just the site

This one is easy to forget because it isn’t about your live app at all. It takes two minutes.

  • Your share link. Click Share in your project and look at what Anyone with the link can do. Bolt’s sharing page says Viewers can open the project, duplicate it, and see the preview and the code. Bolt hides environment variables from Viewers, but your code still shows how the app works. Bolt’s own tip is to choose Editor only if you want anyone with the link to change your project. If you ever posted a project link publicly, look at this setting.
  • Your site’s visibility. In the Publish menu, sites are public by default. Bolt’s publishing page says paid plans can publish privately for invited people only, which is handy while you are still testing. Custom-domain sites and every Free plan site are public. Private hides the site, not a database whose address someone already knows, which is why checks 2 to 6 come first.
  • Your GitHub copy. If you connected GitHub, Bolt says the repository starts out private. If you have since made it public, search it for real keys the same way as in check 4. Git keeps old versions, so a key deleted last week can still be in the history.

08

Read your headers and look for source maps

This matters less than the database, and a missing header won’t leak data by itself. It is still worth ten minutes, because headers tell browsers how to protect your visitors. Read yours from a terminal:

Terminal
curl -sI https://YOUR-APP.bolt.host | grep -iE "^(content-security-policy|strict-transport-security|x-frame-options|x-content-type-options|referrer-policy)"

We look for five:

  • Content-Security-Policy, which MDN describes as instructions telling the browser what your page’s code is allowed to load and do. It is the main defence against someone else’s script running inside your app.
  • Strict-Transport-Security, which tells browsers to stick to HTTPS for your site.
  • X-Frame-Options (or a CSP with frame-ancestors), which stops another site from loading yours in a hidden frame to trick people into clicking.
  • X-Content-Type-Options: nosniff and a Referrer-Policy, two small ones that are easy wins.

On Netlify, you set headers with a plain text file called _headers in the folder Netlify publishes, according to Netlify’s headers docs. In a Vite project, anything in the public folder is copied as-is into that output folder, so ask Bolt to create public/_headers with something like this:

public/_headers
/*
  Strict-Transport-Security: max-age=31536000
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Content-Security-Policy-Report-Only: default-src 'self'; connect-src 'self' https://*.supabase.co wss://*.supabase.co

We served a test app with this file through Netlify’s local dev server, and all five headers came back as written. The content security policy is in report-only mode on purpose: a strict policy can break an app, and MDN suggests this way of trying one first, where the browser reports what it would block in the Console without blocking anything. Once the Console stays quiet, ask Bolt to switch it to a real Content-Security-Policy.

On Bolt hosting, we couldn’t find a setting for custom response headers in Bolt’s docs, so check rather than assume: run the command above, and if something is missing, ask Bolt in Plan mode how to add it. For apps that run entirely in the browser, MDN notes that a CSP can also be set with a <meta http-equiv="Content-Security-Policy"> tag, though that route doesn’t support every feature, including report-only mode.

Source maps turn your compressed JavaScript back into readable original code: handy for debugging, and a gift to anyone curious about your app. Vite leaves them off by default, but one setting turns them on. To check:

Terminal
# 1. Find the name of your main script
curl -s https://YOUR-APP.bolt.host/ | grep -oE '/assets/[^"]+\.js'

# 2. Ask for its source map (use the name you got above, plus .map)
curl -s https://YOUR-APP.bolt.host/assets/index-AbC123.js.map | head -c 60

We tried both cases on a test app. If the map is public, the second command prints text starting with {"version":3. If not, many hosts send your normal page instead, starting with <!doctype html. If you see the first, ask Bolt to turn production source maps off and republish.

What a clean result means

If you got through all eight and found nothing, that is real good news, and you did more than most people do before launch. It means nothing obvious is open from the outside today, not that the app is safe forever. A new table or a late-night fix can quietly undo yesterday’s work. Run the audit on every publish, and repeat checks 3 and 5 whenever you add something that stores or sends data.

Where LockShip fits

We built LockShip because checks 3, 4, 5, 6 and 8 are easy to forget and tedious to repeat. You paste your app’s address, prove you own the domain, and the scan reads your app the way a stranger would. It looks for signs that the app was built with Bolt, finds the Supabase address and public key in your page, sees which tables that key can list, and asks each for at most one row. It searches your JavaScript, including code that only loads on other screens and any public source maps, for secret keys such as a service_role key. It notes public storage buckets, tries /.env and /.git on your site, calls API paths it finds on your own domain without credentials, and reads your headers, CORS settings, cookies, and certificate.

For check 5, it picks out the server functions your code calls and knocks on each one once, with no key, no login and nothing in the request. It uses a HEAD request, which carries no data, so there is nothing for a chatbot function to pass on to OpenAI and nothing to email or save. A 401 means the platform check stopped us before your code ran, which is what you want. If your function’s own code answers instead, the report says so: as high if it replied with success, as medium if it ran and then turned our empty request away. Webhooks and health checks are meant to be public, so those get a note instead. It stops after eight functions or ten seconds, whichever comes first. We added this after writing check 5. We had just told you anyone can call a function’s address, and our own scan wasn’t looking.

It is just as important to say what it doesn’t do. It only reads, so it can’t tell you whether strangers can add, change or delete rows, which check 3 asks you to read in your rules. The report says that in so many words and points you to the SQL in our RLS checklist. It never calls a function with your public key, because that would run it for real, so a function that leans only on the default check still needs the test in check 5. It never signs in as your users, so it can’t do the two-account test. It can’t see your Bolt project’s settings, share link, or site visibility (check 7). If it can’t tell your app came from Bolt, which can happen on a custom domain, its fix prompts use general builder wording. It is a point-in-time, best-effort check, not a penetration test, and a clean result means “nothing obvious from the outside right now”, not “safe”.

The summary is free: a score and the single worst thing we found. If you want everything, the $39 Launch Safe certificate includes every finding with a fix prompt you can paste into Bolt, the RLS SQL, a PDF, and seven days of re-scans while you fix things. A passing re-scan gives you a badge you can put on your site.

Want a second pair of eyes on checks 3 to 6?

Paste your Bolt app’s address, prove you own the domain, and see what a stranger can read. The summary is free.

Run a free scan

Before you rely on a result, read the disclaimer.

Questions people ask

Is the Supabase key I can see in my Bolt app a leak?

Usually not. The publishable key (or the older anon key) is meant to be public. Supabase’s docs say it is safe to put in a web page because it only reaches what Row Level Security allows. The keys that must never reach a browser are the secret key (sb_secret_) and the older service_role key, because both skip Row Level Security entirely.

Bolt’s security audit fixed everything it found. Am I done?

You are in a much better place, so keep running it. But Bolt’s help pages say the audit leaves some items for you to handle, and only you know which data is meant to be public. Read what it changed, then test as a stranger and as a second user, which is what checks 2, 3 and 5 are for.

I use Bolt Database, not my own Supabase project. Does this still apply?

Yes. Bolt’s help center says Bolt-managed databases run on shared Supabase infrastructure, and its Security tab uses the same checks as Supabase’s Security Advisor. Row level security, buckets and server functions work the same way. Only where you click differs: Bolt Database lives behind the database icon in your project.

My Bolt site is published on Netlify. What changes?

Bolt’s Netlify page says Netlify can host sites that run entirely in the browser, but not a database or a traditional backend. Any data your app keeps lives elsewhere, and checks 2 to 6 still apply. What changes is headers: Netlify reads them from a _headers file or its configuration file.

Can other people see my Bolt project’s code?

Only if you let them, but that is easy to do by accident. Bolt lets you give anyone with the link Viewer access, and Viewers can see the preview and the code, though not environment variables. Set the link to Editor and anyone with it can change your project.

Keep reading

Sources

We checked each of these on 17 October 2026, and ran every command above against a Vite test app with fake keys, a local stand-in for the Supabase endpoints, and Netlify’s local dev server. Bolt and Supabase change their products often, so if something here disagrees with their docs, trust the docs and tell us at hi@lockship.dev.