PsProdseal

Guides · Updated October 15, 2026

How to check if Row Level Security is on for every Supabase table

The Supabase key in your app’s browser code is public, and Supabase says it is safe to expose with Row Level Security (RLS) enabled (Securing your data). RLS is the set of rules that decides which rows each visitor can read or change. This guide shows how to confirm RLS is on for every table and how to read your existing rules. You’ll copy a few queries into the SQL Editor; no other coding is needed.

New to this? Start with why your anon key is public and what actually protects your data.

Why a table can end up without RLS

Tables created through the Supabase dashboard have RLS enabled by default. Tables created in the SQL Editor or with another tool need it turned on explicitly (Securing your API). So check every table, including the ones your AI builder created.

Check 1: the Table Editor

Open the Table Editor and click through each table in the public schema. The dashboard shows whether RLS is enabled for the table you have open, and you can turn it on from there. The SQL checks below are faster when you have many tables.

Check 2: the Policies page

Supabase’s docs place the Policies page under Database → Policies; some dashboard layouts show it under Authentication → Policies. It lists your tables with their policies and has an Enable RLS button for any table that has it off.

Check 3: the Security Advisor

The Security Advisor checks your database automatically (Database Advisors). Open it from your project dashboard and look for these findings:

  • 0013 RLS disabled in public: a table in public has RLS off.
  • 0007 Policy exists RLS disabled: a table has policies, but RLS is off, so they do nothing.
  • 0008 RLS enabled no policy: RLS is on, but the table has no policies yet.
  • 0024 Permissive RLS policy: a policy the advisor considers too open.

Rerun it after each fix.

Check 4: SQL that lists every table

Open the SQL Editor and run this query. It lists every table in public, whether RLS is on, and how many policies each table has. It reads the Postgres views pg_tables and pg_policies.

select t.tablename,
       t.rowsecurity as rls_on,
       count(p.policyname) as policy_count
from pg_tables t
left join pg_policies p
  on p.schemaname = t.schemaname
 and p.tablename = t.tablename
where t.schemaname = 'public'
group by t.tablename, t.rowsecurity
order by t.tablename;

To list only tables with RLS off, run this. An empty result means every table in public has RLS on.

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

Check 5: SQL that lists every policy

Next, read what each policy in public allows:

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

The cmd column shows the action (SELECT, INSERT, UPDATE, DELETE or ALL). The roles column shows who the policy applies to, and {public} means every role, including people who aren’t logged in.

Look for any rule that reads true. A policy with using (true) lets everyone it applies to see every row, and Supabase notes that to anon using ( true ) gives every signed-out visitor read access (Row Level Security). This query shows only those policies:

select tablename, policyname, cmd, roles
from pg_policies
where schemaname = 'public'
  and (qual = 'true' or with_check = 'true');

A true rule can be fine for data that is meant to be public, like a list of product names. For anything personal, replace it.

What to do with each finding

A table has RLS off. Turn it on in the SQL Editor, using your table’s name:

alter table public.notes enable row level security;

With RLS on and no policies, Postgres uses a default deny, so no rows are visible or can be changed (PostgreSQL row security). Your app may show no data from that table until you add policies.

A table has RLS on but no policies. Add one policy per action, scoped to the logged-in user with auth.uid(). This example follows the Supabase RLS guide and assumes the table has a user_id column. A delete policy works the same way with using. An update needs a matching select policy to work.

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

create policy "Users can add their own notes"
on public.notes for insert
to authenticated
with check ( (select auth.uid()) = user_id );

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

Supabase recommends wrapping auth.uid() in select as shown, which lets Postgres evaluate it once per query instead of once per row.

A policy uses true on private data. Drop it with drop policy "Policy name" on public.notes; and add scoped policies like the ones above.

Test with real accounts. Table owners and roles with the BYPASSRLS attribute skip RLS (PostgreSQL row security), and Supabase’s service_role, used by secret keys, has that attribute. So test from your app itself, not from the SQL Editor. Create two accounts, add data with the first, then log in as the second and confirm you can’t see or change it. Then log out and confirm the data stays hidden.

The October 30, 2026 change

From October 30, 2026, Supabase applies a new default to existing projects: new tables in public aren’t reachable through the Data API until you grant access (Supabase changelog). Existing tables keep their current grants. A new table without grants returns error 42501, “permission denied for table”.

Grants decide whether a role can reach a table at all, and RLS decides which rows it sees. Fix the error with a narrow grant to authenticated and keep RLS on with scoped policies:

grant select, insert, update on table public.notes to authenticated;
alter table public.notes enable row level security;

Grant only the actions your app needs. Don’t grant all privileges to anon, and don’t turn RLS off to make the error go away. Details in our October 30 guide.

Get a second look

Prodseal is for apps built with Lovable, Bolt, Replit, v0 and similar tools on Supabase. The free check looks at what any browser receives from your public URL, such as keys in your JavaScript and the headers your app sends. Connect GitHub (Prodseal reads code and opens pull requests, never merges) or Supabase (read-only) for a free full report that includes your database access rules. For $49 one time, Prodseal writes and tests fixes for the Critical and High issues it can fix automatically, as a GitHub pull request or as SQL plus a Lovable prompt, with manual steps for anything it can’t fix.

Questions go through prodseal.dev/contact.

Run my free check
How to check RLS on every Supabase table — Prodseal