Missing Row Level Security on Supabase Table
A public Supabase table has RLS disabled or has an overly permissive policy, meaning any authenticated user can read or modify every row regardless of ownership.
Typical error
Row level security disabled
What this is
Supabase's Postgres layer supports row level security (RLS). When enabled, the database itself enforces "can this user see this row?" policies on every query. When disabled, the browser-side Supabase client can read any row by default.
AI-built apps running on Supabase commonly ship with RLS either disabled or with a permissive policy such as:
create policy "allow_all" on public.orders
for all using ( auth.role() = 'authenticated' );That policy lets any logged-in user read every row in orders, regardless of who owns it.
Why AI tools ship this
Prompt-to-app builders like Lovable and Bolt scaffold Supabase apps that "just work" in the dev preview. They confirm the happy path, ship, and skip the policy tuning step.
How to detect
Run in the Supabase SQL editor:
select tablename, rowsecurity
from pg_tables
where schemaname = 'public';Any table with rowsecurity = false is publicly readable by any authenticated user. Cross-check each one against sensitive data models.
How to fix
Enable RLS on the table, then add an ownership policy:
alter table public.orders enable row level security;
create policy "users_read_own_orders"
on public.orders
for select
using ( auth.uid() = user_id );
create policy "users_insert_own_orders"
on public.orders
for insert
with check ( auth.uid() = user_id );Every policy should pin rows to auth.uid(), a foreign key the database can verify. Never trust a userId passed from the client.
Related
- Glossary: row level security
- Glossary: IDOR
- Blog: Supabase RLS Guide