Supabase RLS check: copy-paste SQL to find exposed tables
If your app talks to Supabase from the browser, your anon key is public, and Row Level Security is what decides who can read and write each row. These queries run in the Supabase SQL Editor and give you a definitive answer from inside your project. Unspill never queries your database; this is the check it points you to.
Scan your live app free Passive, no signup, never touches your database.
1. Run the audit queries
Open Supabase → SQL Editor, paste, and run. Each query is read-only.
-- 1) Tables in the public schema without RLS
select tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;
-- 2) Every policy, who it applies to, and its condition
select tablename, policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'public'
order by tablename, cmd;
-- 3) Policies that let everyone through (USING true / WITH CHECK true)
select tablename, policyname, cmd, roles
from pg_policies
where schemaname = 'public'
and (qual = 'true' or with_check = 'true');
-- 4) Storage buckets that serve every file publicly
select id, name, public from storage.buckets order by public desc;
-- 5) Views that may bypass RLS (Postgres 15+: want security_invoker=true)
select c.relname as view, c.reloptions
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relkind = 'v';
-- 6) SECURITY DEFINER functions callable through the API (run as their owner)
select p.proname
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
where n.nspname = 'public' and p.prosecdef;2. Read the results
Query 1: any rowsecurity = false table is reachable with your anon key. Query 3: any row here lets everyone through; fine for truly public content, dangerous for personal data. Query 4: public = true buckets serve files to anyone with the link. Query 5: views without security_invoker=true run with the owner's rights. Query 6: SECURITY DEFINER functions run as their owner; make sure they check permissions.
3. Apply owner-only policies
Template for a table where each row belongs to one user (replace notes and user_id):
alter table public.notes enable row level security;
create policy "Owners can read" on public.notes
for select to authenticated
using ((select auth.uid()) = user_id);
create policy "Owners can insert" on public.notes
for insert to authenticated
with check ((select auth.uid()) = user_id);
create policy "Owners can update" on public.notes
for update to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);
create policy "Owners can delete" on public.notes
for delete to authenticated
using ((select auth.uid()) = user_id);4. Run Supabase's Security Advisor
Dashboard → Advisors → Security Advisor lints your database for issues such as RLS disabled on public tables and SECURITY DEFINER views. Re-run it after every schema change.
5. Test from the outside
Logged out, in a private window, use your app with the browser Network tab open. Any /rest/v1/ response containing other users' data is a leak. Repeat while logged in as a second test user.
Fix guides for these issues
- Verify Supabase Row Level Security: Your Supabase anon key is public by design, so Row Level Security is the only thing protecting your tables. Run these SQL checks to find tables with RLS off or policies that let anyone read everything.
- Supabase service_role key exposed: A Supabase service_role (or sb_secret_) key in front-end code gives anyone full read/write access to your database, bypassing Row Level Security. Here is how to rotate it and fix the leak.
FAQ
Does enabling RLS with no policies break my app?
It blocks all access through the API for anon and authenticated users until you add policies, so add policies right after enabling it.
Why (select auth.uid()) instead of auth.uid()?
Supabase's RLS performance guidance recommends wrapping auth functions in a select so Postgres evaluates them once per query instead of once per row.
Does the service role key respect RLS?
No. The service role bypasses RLS entirely, so it must never be shipped to the browser.
Sources
- Supabase docs: Row Level Security
- Supabase docs: Database Advisors
- Supabase docs: Storage access control
Last checked against these sources on 2026-10-19. Platforms change; when in doubt, the official docs win.