VibeCheck

Guide · Bolt

Bolt app security checklist

Bolt can scaffold a full-stack app in an afternoon. Before real users put real data into it, go through this list. Each item says what to look for, why it matters, and what to ask Bolt to change.

Bolt apps are usually a Vite + React frontend deployed as static files, with a database behind them (commonly Supabase). Everything in the frontend is downloaded by every visitor, so the core rule is simple: the browser is public; protection has to live in the database or on a server.

Secrets

1. No secret keys in the frontend

In a Vite app, any environment variable starting with VITE_ is copied into the JavaScript bundle at build time. That is correct for public values like the Supabase URL and anon key, and wrong for anything secret: Stripe secret keys, OpenAI or Anthropic keys, the Supabase service_role key, email API keys.

Check: open your live site, open the browser's developer tools, and search the loaded JavaScript for sk_live_, sk-, sb_secret_, re_, SG.. Fix: rotate the key at the provider first, then move the call into a server function. Details: which frontend keys are fine.

2. .env is not deployed or committed

Make sure .env is in .gitignore and that your host does not serve it. Try opening https://your-app/.env. Many hosts return the app's own index.html for unknown paths, so look at the content, not just whether the page loads.

Database

3. Row Level Security on every table

If you use Supabase, every table in the public schema needs RLS enabled and sensible policies. Without it, anyone with your public anon key can read and often modify the whole table. Use the queries in the Supabase RLS checker guide to list tables and policies.

Ask Bolt: "Enable Row Level Security on every table and add policies so users can only read, insert, update and delete rows where user_id = auth.uid(). Show me the SQL."

4. No policy that says using (true) on private data

A table with RLS turned on but a policy of using (true) is as open as a table without RLS. That policy is fine for genuinely public content, like a list of published blog posts, and wrong for profiles, orders or messages.

5. Storage buckets are not listable

Uploaded files (avatars, receipts, documents) live in storage buckets with their own policies. User documents belong in private buckets, readable only by their owner. Check that an anonymous visitor cannot list a bucket's contents.

Auth

6. Sign-up settings match your policies

If a policy allows "any authenticated user", then anyone who can create an account gets that access. Decide whether sign-ups should be open, require email confirmation, and whether anonymous sign-ins should be allowed at all.

7. Admin checks are on the server

Generated code sometimes guards the admin page with a check like user.email === "owner@..." in React. That hides the page but not the data. Store roles in the database (a table users cannot write to, or app_metadata) and enforce them in RLS policies or server code. Never compare a password in frontend JavaScript; the password ships with the bundle.

Server functions

8. Every function checks the caller

Server or edge functions that send email, charge cards, call AI models or write data must verify who is calling. For Supabase edge functions, the built-in JWT check accepts the public anon key, so also load the user with supabase.auth.getUser() and reject requests without one.

9. AI calls go through the server, with limits

If the browser calls OpenAI, Anthropic or Gemini directly, the API key is public and anyone can spend your credits. A sign of this in code is dangerouslyAllowBrowser: true. Move the call to a function, check the user, and cap usage per user.

Deployment

10. No public source maps

Source maps (.map files) let anyone read your original source, comments included. Vite does not publish them by default, but check build.sourcemap in vite.config and try opening the main JS file's URL with .map appended.

11. Security headers

Ask your host to send at least:

On Netlify these go in a _headers file or netlify.toml; other hosts have equivalent settings.

12. Check again after every big change

Security is not a one-time step in AI-built apps. Each prompt can add a table, a function or a new key. Re-check the list above after major features, and especially after anything involving payments, uploads or admin features.

Quick summary

AreaGoodBad
Keys in bundleSupabase anon/publishable, Stripe pk_service_role, sk_live_, AI keys
TablesRLS on, owner-based policiesRLS off, or using (true)
BucketsPrivate, owner-onlyAnyone can list files
AdminRole checked in DB/serverEmail compared in React
FunctionsUser verified insideAnyone with anon key can call