Guides / Supabase RLS checklist
Supabase RLS checklist for vibe-coded apps
Updated November 3, 2026
Most apps built with Lovable, Bolt and similar tools use Supabase, and the browser talks to the database directly with the public anon key. That's a normal, supported setup, but only when Row Level Security (RLS) is configured correctly. Use this checklist.
Why RLS matters here
The anon key is designed to be public and will be visible in your site's JavaScript. Supabase's API exposes tables in the public schema. With RLS off, a table is readable and writable by anyone holding the anon key. With RLS on and no policies, nobody can access it through the API. Policies decide everything in between.
The checklist
- RLS is enabled on every table in exposed schemas. Check in the dashboard (Table Editor or Authentication → Policies), or run:
select tablename, rowsecurity from pg_tables where schemaname = 'public';
Any row showingfalseneeds attention. - Every table has the policies it needs, and no more. A policy like
using (true)for select means "anyone can read." That's fine for a public list of products, not for profiles, messages or orders. - Policies are scoped to the user. For user-owned rows, a common pattern is:
create policy "Users read own rows" on public.notes for select to authenticated using ( (select auth.uid()) = user_id );
- Insert and update policies have a
with check. Without it, a user may be able to insert or move rows to another user'suser_id.create policy "Users insert own rows" on public.notes for insert to authenticated with check ( (select auth.uid()) = user_id );
- Roles are set deliberately. Use
to authenticatedfor logged-in-only access. Remember that open sign-ups mean anyone can become "authenticated." - Don't trust user-editable metadata for permissions.
raw_user_meta_datacan be changed by the user. Keep roles like "admin" in a table the user can't write to, or in app metadata set server-side. - Views and functions don't bypass RLS by accident. Views run with their owner's rights by default in Postgres; on Postgres 15+ you can create them with
security_invoker = true. Functions markedsecurity definerskip RLS, so keep them out of exposed schemas or check permissions inside them. - Storage has policies too. Buckets and the
storage.objectstable need their own rules. Private files belong in private buckets. - The service_role key is never in the frontend. It bypasses RLS completely. Use it only in server code or Edge Functions.
- Check the Security Advisor. The Supabase dashboard has a Security Advisor that flags tables without RLS and other common issues. Fix what it reports.
- Test as a stranger. Log in as a second test user, or use the API with only the anon key, and confirm you can't read or change the first user's data.
ShipReady Check can't see your policies from outside, but it does flag Supabase config warning signs visible in your public JavaScript, like a service_role key shipped to the browser.
More guides: Is my Lovable app secure? · Bolt.new app launch checklist · Exposed API keys: how to check · Pre-launch checklist: Cursor, v0, Replit