ShipShape

Guide

Is my Bolt app secure?

A Bolt app can be safe to publish. A grade of A does not prove it. The free check reads the public URL. It does not see your tables, your auth settings, or anything behind login.

What commonly goes wrong

Bolt’s database guide lists VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY, and SUPABASE_SERVICE_ROLE_KEY as environment variables. Vite’s own docs say a name that starts with VITE_ is exposed to client source and is included in the built bundle. The anon key is meant for the browser. The service role key is not, and neither is a provider secret such as a Stripe secret key or an OpenAI key. Sources: Bolt’s introduction to databases and Vite env variables.

Bolt’s Secrets settings page says secrets are read by server functions and should not end up in the browser. A secret pasted into a component, or copied into a VITE_ name, is in the bundle anyone can download. Source: Bolt database secrets.

If the project is Next.js rather than Vite, the same rule applies to NEXT_PUBLIC_. Next.js inlines those values into the client bundle at build time. Source: Next.js environment variables.

A Supabase backend, whether that is Bolt’s database or a Supabase project you connect, still needs row level security and grants. Public source maps and missing security headers are the other gaps that show up on the published URL. Bolt’s database guide says you can prompt Bolt to create a Bolt database or connect your own Supabase database. The scan cannot tell those apart, and it cannot see the policies.

What the free Quick Check can and cannot see

The free Quick Check fetches the public HTML, same-origin JavaScript, security headers, cookie flags, and whether the URL stays on HTTPS. It also requests seven fixed paths: /.env, /.env.local, /.env.production, /.git/config, /.git/HEAD, /.aws/credentials, and /.npmrc. Source maps named by those scripts are included. Matched secrets are masked. The free page writes the fix for the single most serious finding.

It does not query tables. It does not test row level security. It does not log in, brute force, fuzz, or enumerate paths beyond that list. An anon Supabase key in the bundle is client configuration. A service_role key in the bundle is a finding. Neither one tells you whether policies exist.

The €19 Launch Report opens the rest of that same pass. It does not scan anything new. Monitor, €9 per month, repeats the same passive check each week and when a deploy hook fires. It will not notice a table with row level security off.

What to fix

Run the free check on the published URL, not the editor preview. Rotate any secret the report shows.

Remove that secret from client code and from any VITE_ or NEXT_PUBLIC_ name. Bolt’s Secrets page describes storing a server-function secret outside the browser bundle. A host environment variable that the server reads, and that is not given a public prefix, is the same idea on Vercel or anywhere else you deploy the export.

Then confirm headers, source maps, and Supabase yourself. The check can see the first two. It cannot see the third.

Source maps, headers, and Supabase

If a .js.map file is public, turn off maps that ship to browsers, or keep stack traces without serving the files. In Vite set build.sourcemap to 'hidden' (or false to omit maps). In webpack set devtool to 'hidden-source-map'. In Next.js leave productionBrowserSourceMaps off, which is the default. Upload the maps to your error tracker in CI, then delete the .map files from the deploy output. A hidden map is still public if the file is deployed beside the script. Vite documents 'hidden' as a map file without the sourceMappingURL comment. Webpack documents hidden-source-map the same way. Next.js documents that production browser source maps stay off unless you opt in.

Missing headers are fixed on the host that serves the published URL. The check looks for Content-Security-Policy, Strict-Transport-Security on HTTPS, clickjacking protection (X-Frame-Options or CSP frame-ancestors), X-Content-Type-Options, and Referrer-Policy.

If the app uses Supabase, open the RLS check and the 30 October grant note. Confirm row level security and the GRANT yourself. Disabling RLS is not the fix for a missing grant. This scan does not query tables, so it cannot do that confirmation for you.

Questions

Does the free check test row level security on a Bolt or Supabase database?

No. The free check does not query tables and does not test row level security. You confirm policies and grants in the database yourself.

Is a VITE_ variable private because it is in an env file?

No. Vite exposes names that start with VITE_ to client code, and the built bundle includes those values. A secret does not belong in a VITE_ variable. The anon Supabase key is the usual exception, because the browser is supposed to hold it.

Run a free check