Home / RLS / Fix a permissive USING (true) policy
Supabase RLS · Fix
USING (true) is a security hole — how to fix it
USING (true) means 'everyone, always' — it quietly makes the table public read. Find them with a query on pg_policies and replace each with a policy scoped to (select auth.uid()) or workspace membership.
01 Why USING (true) leaks
An RLS policy's USING clause is the filter for which rows a caller may see. USING (true) evaluates true for every row and every caller — so anyone with your anon key can read the whole table. It's the second most common Supabase leak, right after RLS being off, and it usually gets copy-pasted in from a tutorial that just wanted the demo to work.
02 Find every permissive policy
audit.sql
select schemaname, tablename, policyname, cmd, qual from pg_policies where schemaname = 'public' and qual = 'true'; -- USING (true)
Also check with_check = 'true' for permissive writes. Anything that comes back is a table any user can read (or write).
03 Replace it with a scoped policy
migration.sql
drop policy "public read" on notes; create policy "owner reads own notes" on notes for select using (user_id = (select auth.uid()));
04 If you actually want public read
Sometimes public read is the point (a blog, a public profile). Do it on purpose and scope the columns — don't expose the whole row. Restrict to a published flag and select only safe columns:
migration.sql
create policy "anyone reads published posts" on posts for select using (is_published = true);
That still isn't USING (true) — it's a deliberate rule. Keep private columns out of any table that has a public policy.
Catch this before it ships
airlock-rls is a CI gate that fails your build when a table ships exposed or a policy is permissive — the same class of bug, caught on the pull request instead of in prod.
Or start from nextjs-supabase-starter — auth + a table with RLS + an isolation test, so a fresh table is safe by default.
Grab the Supabase RLS cheat sheet
The golden rules, the footguns that leak in prod, correct policy snippets, and the isolation test — on one page.
FAQ
How is USING (true) different from RLS being off?
Both make the table publicly readable. RLS off means the engine doesn't check policies at all; USING (true) means RLS is on but the policy allows everyone. The audit is different — one shows up in pg_class.relrowsecurity, the other in pg_policies.qual — but the leak is the same.
Can I keep USING (true) for an internal table?
No — 'internal' tables are still reachable through the anon key. If a table is exposed to Supabase's API at all, USING (true) makes it public. Scope it to the caller, or don't expose it.