How to check that Row Level Security is enabled on every Supabase table

· 5 min read

Row Level Security (RLS) is what keeps your public Supabase key from reading everything in your database. AI tools usually switch it on, but not always, and a policy that says "allow everyone" is as bad as no RLS at all. Here is how to check, in about five minutes.

What RLS does

Your front end talks to the database with the anon (publishable) key, which is visible to every visitor. RLS decides which rows that key may see. With RLS on and no policy, the answer is none. With RLS off, a table in the public schema is open to anyone who has the key. See anon key vs service_role key for how the two keys differ.

Check 1: the dashboard

Open the Table Editor. Tables with RLS off show a warning badge. Also open the Security Advisor, which flags tables without RLS and policies that look too broad. Fix every red item before you launch.

Check 2: list tables with RLS off (SQL)

Run this in the SQL Editor. Any row with rowsecurity = false is a table open to the public key.

select schemaname, tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;

Check 3: read your policies

RLS on is not enough if the policies are too wide. List them and read the qual and with_check columns.

select tablename, policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'public'
order by tablename, policyname;

Be suspicious of true as a condition, which allows every row, and of policies on UPDATE or DELETE that have no auth.uid() check.

Check 4: grants and views

Policies only apply to tables. Check what the anon and authenticated roles can do:

select table_name, grantee, privilege_type
from information_schema.role_table_grants
where table_schema = 'public' and grantee in ('anon', 'authenticated')
order by table_name, grantee;

Views run with their owner's rights by default, so a view over a protected table can bypass RLS. On recent Postgres versions, create it with security_invoker = true.

Check 5: try it as a stranger

Request a table with only the public key, the way a visitor could. Use your own project and your own table name:

curl "https://YOUR-PROJECT.supabase.co/rest/v1/YOUR_TABLE?select=*" \
  -H "apikey: YOUR_ANON_KEY"

You want an empty list ([]) or a permission error. If you get rows back that a signed-out visitor should not see, the table is exposed.

How to fix a gap

alter table public.orders enable row level security;

create policy "Owners read their own orders"
  on public.orders for select
  to authenticated
  using ((select auth.uid()) = user_id);

Add similar policies for insert, update and delete, each limited to the owner. Never "fix" an access error by turning RLS off or by using the service_role key in the browser. If you already did, read what to do if the service_role key leaked.

Check what visitors can see

A free FixPrompt scan finds the Supabase host in your public code and flags privileged keys. It never queries your data, so use the checks above for the database itself.

Prompt for your coding agent

Audit Row Level Security in this Supabase project:
1. List every table in the public schema and whether RLS is enabled.
2. Enable RLS on every table that has it off.
3. For each table, add owner-only policies for select, insert, update and delete using
   auth.uid(). Flag any table that is meant to be public and explain why.
4. Find views over protected tables and set security_invoker = true.
5. Search the front-end code for service_role or sb_secret_ keys and move them server-side.
Show me the SQL before running it and explain each policy in plain English.

Check your app in minutes

Paste your URL and get a plain-English report with prioritized risks and a fix prompt for your AI. Your first scan is free.

Scan my app free