Is my Bolt app secure?
Written by our founder, a cybersecurity architect · Updated
Short answer
Not automatically. Bolt builds a working app quickly, usually a React frontend built with Vite that talks to Supabase or another backend straight from the browser. Whether it is secure depends on a few things Bolt can get wrong: which keys reach the browser, what your database lets a visitor read, and whether your server functions check who is calling.
The most common problem is a secret key (OpenAI, Stripe, Supabase service_role) stored in a VITE_ variable or written into a component. Vite copies it into the JavaScript every visitor downloads.
The checklist below goes through what to check, in order of how much damage each mistake does.
Why do Bolt apps leak keys?
When you ask Bolt for a feature that calls OpenAI or Stripe, the quickest version that works calls the API from the frontend, with the key in a VITE_ variable. It works in the preview, and once deployed it hands the key to every visitor.
The second cause is the database. With Supabase, the browser talks to your tables directly, so the only thing standing between a visitor and your data is Row Level Security (RLS).
1. Are secret keys kept out of the frontend?
Open your .env file and look at every variable that starts with VITE_. Only values meant to be public belong there, such as your Supabase URL and anon key. Anything else moves to server code:
- A Supabase Edge Function, with the key stored in Edge Function secrets.
- A serverless function on your host, such as Netlify Functions, with the key in the host’s environment variables without the
VITE_prefix.
If a secret was ever in your frontend, rotate it: deleting the line doesn’t remove it from old builds. Our guide on finding API keys in your JavaScript shows how to search the live site and the build.
2. Is RLS on for every Supabase table?
Run this in the Supabase SQL editor. Any table with false can be read and changed by anyone who has the anon key your site ships:
select tablename, rowsecurity as rls_enabled
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;Then check what each policy allows, and test a table with only the anon key from a terminal. Our Supabase RLS guide walks through both.
3. Do your server functions check the user?
- Read the signed-in user from the request and refuse the call if there is none.
- When a function takes an id (an order, a document), check that the record belongs to that user.
- Functions that call an AI model cost you money on every call. Require a signed-in user and set a limit per user.
- Compute prices on the server from your own product list, and verify the signature of Stripe webhooks.
4. Is your .env file kept private?
Vite serves everything in the public/ folder as is, so a .env or backup file placed there can be downloaded by anyone. If you export your project to GitHub, check that .env is listed in .gitignore before the first push.
5. What does your live site expose?
Finally, look at the deployed site the way a stranger would: keys inside the JavaScript, source maps that reveal your source code, files like .env left public, and missing security headers.
Our free scan checks those in about a minute, with no account. It doesn’t test your database or your logins: Supabase tables readable without an account are checked by our backend checks after you prove you own the site, once a month on the free plan and weekly on Pro.
Checklist
- Only public values (Supabase URL, anon key, Stripe
pk_) are inVITE_variables - OpenAI, Stripe secret and Supabase
service_rolekeys are only in server functions - Keys that were ever in the frontend have been rotated
- RLS is on for every table in
public - Policies check ownership, not just “signed in”
- Server functions check the user and limit AI calls
- Prices are computed on the server; Stripe webhooks are verified
- No
.envor backup file inpublic/or in Git
Scan your site for free
Leaked API keys, exposed files and missing security headers, with a fix for each. No account, about a minute.
Questions
- Are VITE_ environment variables secret?
- No. Every variable starting with VITE_ is copied into the JavaScript the browser downloads. Use them only for values meant to be public, like your Supabase URL and anon key.
- Where should I put my OpenAI key in a Bolt app?
- In server code: a Supabase Edge Function secret, or your host’s environment variables without the VITE_ prefix. The frontend calls your function, and only the function calls OpenAI.
- Is the Supabase anon key in my Bolt app a problem?
- No, it is public by design. It only gives the access your Row Level Security policies allow, so the policies are what you need to check.
- Can I scan my Bolt app for free?
- Yes. Paste its live address into our free scan to check for leaked keys, exposed files and missing security headers, with a fix for each issue. It doesn’t test logins or your database rules.