Home › Guides

Supabase RLS checklist: lock down your database before launch

Updated 2026-10-21 · 8 min read

Your Supabase URL and anon key ship to every browser. That’s by design: Supabase relies on Postgres Row Level Security to decide which rows each request can touch. If RLS is off, or a policy is too loose, your anon key is effectively a master key. Use this checklist before you launch and after every AI-generated migration.

Check yours in 30 seconds: paste your live URL into the free ShipGrader scan. It reads only what your site already serves to every visitor and gives you an A–F launch grade with the top fixes.

1. Enable RLS on every table in exposed schemas

Tables created in the SQL editor or by migrations don’t always have RLS on. Find the ones that don’t:

select schemaname, tablename from pg_tables where schemaname = 'public' and rowsecurity = false;

Then enable it on each one:

alter table public.profiles enable row level security;

With RLS enabled and no policies, the table is locked to everyone except the service_role. That’s a safe default, and you add access back one policy at a time.

2. Add owner-only policies

The most common pattern: each row has a user_id column referencing auth.users, and users can only see and change their own rows.

create policy "Users read own rows" on public.notes for select to authenticated using ( (select auth.uid()) = user_id ); create policy "Users insert own rows" on public.notes for insert to authenticated with check ( (select auth.uid()) = user_id ); create policy "Users update own rows" on public.notes for update to authenticated using ( (select auth.uid()) = user_id ) with check ( (select auth.uid()) = user_id ); create policy "Users delete own rows" on public.notes for delete to authenticated using ( (select auth.uid()) = user_id );

Wrapping auth.uid() in (select …) lets Postgres cache it per query. Supabase recommends this for performance. Also add an index on user_id.

3. Hunt for dangerous policies

  • using (true) or with check (true) on insert, update or delete means anyone can write.
  • Policies targeting the anon role on tables holding personal data.
  • Update policies with using but no with check. A user could reassign a row to someone else.
  • Role or plan columns (is_admin, plan, credits) that users can update on their own profile row. Move these to a separate table only the backend writes.

4. Secure Storage buckets

Storage access is controlled by RLS policies on storage.objects. Public buckets serve any file to anyone with the URL, which is fine for avatars but wrong for invoices or uploads. For private per-user folders:

create policy "Users read own files" on storage.objects for select to authenticated using ( bucket_id = 'uploads' and (storage.foldername(name))[1] = (select auth.uid())::text );

5. Watch views and security-definer functions

Views run with the permissions of their creator by default, which can bypass RLS. On Postgres 15+, create them with with (security_invoker = true). Functions marked security definer also bypass RLS. Keep them out of exposed schemas or check auth.uid() inside them.

6. Keep the service_role key server-side

The service_role (or new sb_secret_) key ignores RLS entirely. It belongs only in Edge Functions or your backend. Never put it in a VITE_ or NEXT_PUBLIC_ variable. ShipGrader decodes every JWT in your bundles and flags service_role as critical.

7. Run the Security Advisor and test as a stranger

  1. Dashboard → Advisors → Security Advisor: fix every error and warning.
  2. Sign up a second test account and confirm it can’t see the first account’s data.
  3. Log out and confirm the anon role sees only what you intend to be public.

Copy-paste checklist

  • RLS enabled on every table in public
  • Owner policies for select / insert / update / delete
  • No true write policies
  • Privileged columns not user-editable
  • Storage buckets private unless truly public
  • Views use security_invoker
  • service_role key only on the server
  • Security Advisor clean

Want the full fix list?

The $29 Full Report lists every finding with step-by-step fixes, plus Privacy Policy, Terms and Refund starter text written for the services we detect in your app.

Scan my app free →