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

  1. 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 showing false needs attention.
  2. 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.
  3. 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 );
  4. Insert and update policies have a with check. Without it, a user may be able to insert or move rows to another user's user_id.
    create policy "Users insert own rows" on public.notes
      for insert to authenticated
      with check ( (select auth.uid()) = user_id );
  5. Roles are set deliberately. Use to authenticated for logged-in-only access. Remember that open sign-ups mean anyone can become "authenticated."
  6. Don't trust user-editable metadata for permissions. raw_user_meta_data can 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.
  7. 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 marked security definer skip RLS, so keep them out of exposed schemas or check permissions inside them.
  8. Storage has policies too. Buckets and the storage.objects table need their own rules. Private files belong in private buckets.
  9. The service_role key is never in the frontend. It bypasses RLS completely. Use it only in server code or Edge Functions.
  10. Check the Security Advisor. The Supabase dashboard has a Security Advisor that flags tables without RLS and other common issues. Fix what it reports.
  11. 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