What a Bolt app ships with
- A Vite + React (sometimes Next.js) app built inside the browser and deployed to Netlify with one click.
- Supabase or Firebase wired in through the integrations, with the public keys placed in the client code.
- A .env in the WebContainer whose VITE_ values are compiled into the bundle.
- Netlify hosting, where security headers come from a _headers file that Bolt does not write unless asked.
The five things that go wrong
- Firebase rules still in test mode. The Firebase console's "test mode" allows every read and write until a date. Apps ship with it on. The scan asks the Realtime Database for its top-level section names and Firestore for one masked document to see whether unauthenticated reads are allowed.
- Supabase tables without Row Level Security. Same story as any Supabase app: the anon key in the bundle is public, and a table without policies is public with it.
- Keys in VITE_ variables. Anything that starts with VITE_ is copied into the JavaScript every visitor downloads. A Stripe secret key or an OpenAI key there is a matter of when, not if.
- No _headers file. Without it Netlify sends no Content-Security-Policy, no HSTS, no clickjacking protection. One eight-line file fixes all of them.
- Source maps in production. Bolt builds do not ship .map files by default, but a copied vite.config with sourcemap: true does, and it hands over your un-minified source.
How to fix them
Lock Firestore down to signed-in owners
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /posts/{docId} {
allow read, write: if request.auth != null && request.auth.uid == resource.data.ownerId;
allow create: if request.auth != null && request.auth.uid == request.resource.data.ownerId;
}
match /{document=**} { allow read, write: if false; }
}
}Add security headers on Netlify
public/_headers
/*
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-originKeep a paid key in a Netlify Function
// netlify/functions/ask.js (set OPENAI_API_KEY in Site configuration > Environment variables)
export default async (req) => {
const r = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json" },
body: await req.text(),
});
return new Response(await r.text(), { status: r.status, headers: { "Content-Type": "application/json" } });
};
Every finding in a scan comes with the exact lines for your host and framework, opened for the one it detected.
Questions people ask
- Bolt says my app is deployed. Is it also secure?
- Deployed means reachable. Whether the database is readable by strangers is decided by rules you set in Supabase or Firebase, and a scan is the quickest way to see where they stand.
- Can the scanner see my Netlify functions?
- It sees what a visitor sees: the pages, the JavaScript, the API routes those call, and how they answer. Server code stays private, which is the whole point of putting keys there.
- Is it safe to run on the live app?
- Yes. Read-only requests, a count instead of rows, nothing written, nothing guessed.
Check your Bolt app now
Free, read-only, ten seconds. Then, if you want it watched: weekly re-scans, uptime and error alerts, code checks on the repo.
Scan your app free