Fix guides

Supabase RLS not working in your Lovable, Bolt, or Cursor app

Users can see each other's data, or every query returns nothing. How to check and fix Supabase Row Level Security in an AI-built app.

What you are seeing

Why it happens

  1. RLS is disabled on the table, so the public anon key can read everything.
  2. RLS is enabled but there is no policy for that operation, so Postgres denies it silently (selects come back empty).
  3. The policy compares the wrong column, for example id = auth.uid() when the owner column is user_id.
  4. The insert does not set user_id, so the with check clause fails.
  5. The service role key was used in the browser to "make it work", which bypasses RLS entirely and exposes the database.

How to fix it

1. Check which tables have RLS off

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

2. Turn it on for each user table

alter table public.todos enable row level security;

3. Add owner policies for read and write

create policy "owner can read" on public.todos
  for select using (auth.uid() = user_id);

create policy "owner can insert" on public.todos
  for insert with check (auth.uid() = user_id);

create policy "owner can update" on public.todos
  for update using (auth.uid() = user_id) with check (auth.uid() = user_id);

create policy "owner can delete" on public.todos
  for delete using (auth.uid() = user_id);

4. Default the owner column so inserts pass

alter table public.todos alter column user_id set default auth.uid();

5. Search the frontend for the service role key

grep -rn "service_role\|SERVICE_ROLE" src/ app/ .env* 2>/dev/null

How to know it is fixed

Sign in as two different test users in two browsers. Each should see only their own rows, and a direct REST call with the anon key and no session should return nothing.