← Stack guides

Bolt

Security for your Bolt app

Bolt.new spins up a full-stack app in the browser and deploys it in one click, but that speed hides security decisions: keys you paste into the chat that end up in the frontend, zero headers, and a SPA that AI cannot see. Scan your app and know for sure in 30 seconds.

Typical risks in Bolt

Keys pasted in the chat that end up in the frontend

To "connect" a service you paste the key into Bolt, and it often wires it into client code. Everything that reaches the browser is public: that key is on display.

Deploy without security headers

Bolt's deploy serves your app over HTTPS but without CSP, HSTS or X-Frame-Options. These headers are not there by default and must be added at the host.

SPA invisible to search engines and AI

Bolt generates SPAs whose content only appears after JavaScript runs. AI crawlers and some search engines see a near-empty page and cannot index or cite you.

"Bolt security" is two things worth keeping apart. The first is the Bolt project itself: what you paste into the chat, where environment variables end up, whom you share the project with and where it gets deployed. The second is the code Bolt generates, almost always a Vite and React SPA connected to Supabase, which ships with the same five or six flaws as always. This guide covers both, with an instructions file so Bolt applies the rules on every generation, a review prompt before deploying and the way to check the result from outside.

Part 1: securing your Bolt project

Bolt runs in your browser, and that changes where keys live

Bolt runs the whole project inside the browser with WebContainers. There is no server of your own while you develop, so any key you put in a .env file is in the project, and if it carries the VITE_ prefix it ends up embedded in the bundle that gets deployed. For the browser that is fine only with public keys (Supabase's anon, Stripe's publishable). Secret ones have no place in the frontend: they go in a Supabase Edge Function or a Netlify function, and the client calls the function.

What you paste into the chat ends up in the code

If you paste a key into the conversation to "connect" a service, the model writes it where it needs it, which is usually a component. From there it travels to every visitor. The rule: public keys go in .env; secret ones go in the dashboard of the service that runs the function (Supabase or Netlify), never in the chat. And if you pasted one by mistake, rotate it: the history keeps it even if you delete it from the code.

Sharing, exporting and deploying

A shared project link shows the full code, including the .env. Before sharing, review what is inside. If you export to GitHub, make the repository private and keep .env in .gitignore. The one-click deploy goes to Netlify, on a .netlify.app subdomain or your own domain; Netlify gives you TLS and the redirect to HTTPS, but no security headers, and production environment variables are configured in its dashboard, not in the project's .env.

Supabase is yours

The Supabase integration creates tables, policies and functions from the chat. RLS, buckets and Auth settings remain your responsibility, and Bolt makes the same mistakes as Lovable with policies. The Supabase security guide has three SQL queries to audit the project in a minute.

Part 2: the flaws Bolt puts into your code

  • Secret keys in components. A call to OpenAI or Stripe from React with the key inside. Search for sk_, service_role and Bearer in your deployed bundle.
  • Tables without RLS or with using (true) policies. When a query fails on permissions, the model opens the policy instead of fixing it.
  • Edge Functions without authentication. The template answers anyone with Access-Control-Allow-Origin: *; if the function spends money, anyone can spend it.
  • Checks only in the interface. The admin page is hidden if you are not an admin, but the API answers all the same.
  • Source maps in production. Some Vite templates ship build.sourcemap: true; it publishes your original code next to the minified one.
  • Invisible to AI and search engines. The HTML arrives empty and ChatGPT's or Perplexity's bots see nothing. If the landing page matters, prerender it or separate it. Detail in why AI-built apps are invisible to AI.
  • No security headers. No CSP, no HSTS, no X-Frame-Options. On Netlify they are added with a file.

A block so Bolt always applies it

Bolt reads your project's .bolt/prompt file as permanent instructions. Create the file and paste this:

Security rules for this project:
- Never put secret keys in frontend code or in VITE_ variables. Integrations with secret keys go in a Supabase Edge Function or a Netlify function that reads the key from its environment variables.
- Every new table has RLS enabled and one explicit policy per operation. Do not use using (true) or with check (true) except on public read-only content, and tell me when you do.
- Every server function verifies the user's JWT, restricts Access-Control-Allow-Origin to our domain and validates its input.
- Admin permissions are checked in RLS or on the server, never only in the interface.
- User files go to private buckets with policies and signed URLs.
- build.sourcemap stays disabled in production.
- Put a real title, meta description and Open Graph tags in index.html.

And the headers for Netlify, in a public/_headers file that deploys with the app:

/*
  Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  X-Content-Type-Options: nosniff
  X-Frame-Options: DENY
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()

A review prompt before you deploy

Review the security of this project and do not change anything yet. Answer with a list:
1. Keys or tokens in frontend code or in VITE_ variables, with file and line.
2. Tables without RLS and policies with using (true) or with check (true).
3. Server functions that do not verify the JWT, answer with Access-Control-Allow-Origin: * or do not validate input.
4. Admin actions protected only in the interface.
5. Public buckets containing user files.
6. build.sourcemap enabled and a missing _headers file.
7. index.html metadata still left from the template.
For each point, propose the exact change and wait for my confirmation.

Check the result from the outside

curl -sI https://yourdomain.com | grep -iE "strict-transport|x-frame|x-content-type"
curl -s https://yourdomain.com | grep -oE '/assets/[^"]+\.js' | head -1
curl -s https://yourdomain.com/assets/index-XXXX.js | grep -oE "sk_live_[A-Za-z0-9]+|service_role|eyJhbGciOi[A-Za-z0-9_-]{20,}" | head -3
curl -sI https://yourdomain.com/assets/index-XXXX.js.map | head -1

Expected: headers present, no key in the chunk and a 404 on the source map. Then probe the Supabase API without a session as the Supabase guide explains. Scanaris runs these checks and about forty more in 30 seconds: it detects that the app is built with Bolt, looks for secrets in the bundle, checks source maps, headers, CORS, cookies and exposed files, probes the Supabase tables without a session and reviews whether your content is readable without JavaScript. For every failure it gives you a fix prompt to paste into the Bolt chat, and it can rescan every day, because a regeneration can bring back a fixed flaw.

Frequently asked questions

Are VITE_ variables private?

No. Vite embeds in the bundle everything starting with VITE_; it is its way of saying "this is public". Secret keys do not carry that prefix and are not read from the frontend.

Can I set security headers on Netlify?

Yes, with the _headers file in the public folder or with netlify.toml. Netlify applies them on the next deploy; check with curl.

What do I do if I pasted a secret key into the Bolt chat?

Rotate it at the source service even if you deleted it from the code: the project history and earlier deployments keep it. Then add it in the Supabase or Netlify dashboard and ask Bolt to move the integration to a function.

Related checks

Does your Bolt app pass these checks?

Scan it free and get a fix prompt for every issue we find.

Scan my site free