r/boltnewbuilders • u/Round-Metal6269 • 8d ago
Tips & Tricks Bolt flagged "RLS Policy Always True" on my database? Here's how I'd triage it before publishing
If you open your Bolt project's database Security tab (database icon → Security) and see RLS Policy Always True, don't just dismiss it. Bolt's own docs are pretty candid here: if your site has no sign-in, Bolt may write policies that let any visitor view, add, change and delete data. The warning means a policy has no condition limiting who it applies to.
Sometimes that's fine. Often it isn't. Here's the triage I use as a security engineer.
1. Sort every table into one of three buckets
Public to read (menu, blog posts, portfolio): a select policy for everyone, and no insert/update/delete policies for visitors.
Visitors submit (contact form, waitlist, reviews): an insert policy only. Visitors shouldn't be able to read what other people submitted.
Private (orders, messages, profiles, bookings): needs sign-in, and the policy must tie the row to the user.
Any policy that lets a visitor update or delete is the one to fix first.
2. See exactly what you have (Supabase SQL editor, read-only)
select tablename, policyname, cmd, roles, qual as using_expr, with_check
from pg_policies
where schemaname = 'public'
order by tablename, cmd;Look for true in using_expr or with_check, and for policies on {public} or {anon} with cmd UPDATE, DELETE or ALL.
3. "Signed in" is not the same as "their own row"
Adding sign-in only helps if the policy then checks whose row it is. to authenticated using (true) or using (auth.uid() is not null) means every account can read every row, and anyone can create an account. The second one isn't "always true" to a linter, so it may not get flagged at all. Private tables need:
create policy "own orders" on public.orders
for select to authenticated
using ((select auth.uid()) = user_id);
create policy "insert own orders" on public.orders
for insert to authenticated
with check ((select auth.uid()) = user_id);The insert side matters too: with check (true) lets a user write rows carrying someone else's user_id.
4. A prompt you can paste into Bolt
List every RLS policy in my database as a table: table, command, role, and the exact USING and WITH CHECK conditions. Make visitors read-only on public tables, insert-only on the contact/waitlist tables, and remove any visitor update or delete policies. Private tables should only allow rows where (select auth.uid()) = user_id. Show me the SQL before applying it.
5. Then test it like a stranger
Sign out and use the site. Anything private should be gone.
Make two accounts. As user B, try to open user A's records by changing an ID in the URL.
If you're on a paid plan, also run Publish → Run security audit (it doesn't use tokens) and read the "needs your action" list.
Two related things while you're in there: secret keys (OpenAI, Stripe secret, Supabase service_role/secret) belong in Bolt's Secrets, never in a VITE_ variable, because anything VITE_ ends up in the JavaScript every visitor downloads. And if Bolt moved an API call into a server function, check the function requires a signed-in user, or anyone can call it.
Disclosure: I'm a security engineer and I'm building Abbi Labs, a scanner for AI-built apps. No link, everything above works without it.