Guide Supabase

I turned on RLS and now my app shows no data. Is it gone?

Short answer

You saw a security warning, switched Row Level Security (RLS) on, and your Lovable, Bolt, Replit or Cursor app went blank. Lists are empty. Dashboards show nothing. Your data is likely still there. RLS is doing exactly what it was built to do. It just doesn't have any rules yet saying who may see what.

The short answer

When RLS is on and a table has no policies, Supabase's API returns no rows. It doesn't throw an error. Your app asks "show me the projects" and gets back an empty list ([]).

So the app looks broken, but nothing was deleted. [S1]

Check that your data is still there

Open the Supabase dashboard and go to the Table Editor. Pick the table that looks empty in your app.

Queries you run in the Supabase dashboard execute as the postgres role, which has bypassrls, so RLS doesn't filter what you see there. If your rows show up in the Table Editor but not in your app, that confirms it: the data is fine, and the app just isn't allowed to read it yet.

Why this happens

Think of RLS as a locked door with a guest list. Turning RLS on locks the door. Policies are the guest list.

No policies means an empty guest list, so nobody gets in. Your app talks to Supabase as either a signed-out visitor (the anon role) or a signed-in user (the authenticated role). Neither gets any rows until a policy lets them. [S1]

The safe fix: add policies, keep RLS on

Add one policy for each thing your app does with the table: read, add, change, delete. For a typical app table, the rule is simple: each user sees and changes only their own rows.

Here's what that looks like. Replace your_table with your table's name, and user_id with the column that stores who owns the row.

-- Keep RLS on
alter table public.your_table enable row level security;

-- Read: users see only their own rows
create policy "Users read own rows"
  on public.your_table for select
  to authenticated
  using ((select auth.uid()) = user_id);

-- Add: users can only create rows that belong to them
create policy "Users insert own rows"
  on public.your_table for insert
  to authenticated
  with check ((select auth.uid()) = user_id);

-- Change: users can only edit their own rows, and can't hand them to someone else
create policy "Users update own rows"
  on public.your_table for update
  to authenticated
  using ((select auth.uid()) = user_id)
  with check ((select auth.uid()) = user_id);

-- Delete: users can only delete their own rows
create policy "Users delete own rows"
  on public.your_table for delete
  to authenticated
  using ((select auth.uid()) = user_id);

A few plain-language notes:

  • using decides which existing rows a user can see or touch.
  • with check decides which new or changed rows are allowed.
  • Write both on an update policy. Without with check, Postgres reuses the using rule to check the changed row, but writing it out makes the rule clear. If another update policy on the same table has a loose with check (true), a user could still move their row to someone else. [S1] [S2]
  • Only add the policies your app actually uses. If users don't delete anything, skip the delete policy.

More patterns (teams, admins, shared rows) are in our RLS policy examples guide.

What about data that's meant to be public?

Some tables really are for everyone, like a public product list or blog posts. For those, add a read-only policy for signed-out visitors:

create policy "Anyone can read products"
  on public.products for select
  to anon, authenticated
  using (true);

Only do this when every row in that table is fine for the whole internet to see. Don't give anon insert, update or delete just to make a form work.

Re-test three ways

  1. Signed out: private tables should show nothing. Public tables should show their data.
  2. Signed in as user A: you see your own data, and the app works again.
  3. Signed in as user B (a second test account): you can't see or change user A's data.

If step 3 fails, a policy is too loose. Fix that before you launch.

Please don't "fix" it by switching RLS off

It's tempting. Turning RLS off makes the data appear again. It also lets anyone with your project URL and public key read every row, and possibly change or delete it too. That's the exact hole the warning was about. See Table publicly accessible.

Two other "quick fixes" to skip:

  • grant all ... to anon: this gives every signed-out visitor every privilege on the table. It doesn't make policies unnecessary, and if RLS is weak it opens your data wide.
  • Putting the service_role key in your app's frontend: this key bypasses RLS completely. It belongs on the server only (an edge function or backend), not in browser code. If it ends up in the browser, anyone can copy it. [S3]

Seeing "permission denied" (42501) instead of empty results?

That's a different problem.

From Oct 30, 2026, Supabase plans to stop automatically exposing new tables in the public schema of existing projects to the Data API (the API your app calls). Once the change reaches your project, a new table needs an explicit Postgres grant before your app can reach it. [S4]

Without the grant, you get an error, 42501 permission denied for table your_table, rather than an empty list. Empty results point to a missing policy. A 42501 on a new table points to a missing grant. An insert or update blocked by a with check rule also returns 42501, but the message reads new row violates row-level security policy for table "your_table", so read the full message.

If that's what you're seeing, follow our 42501 after Oct 30 rescue guide.

Not sure your new policies are right?

Getting the data back on screen is step one. Making sure each user sees only their own data is step two. Run the free check, or get the $49 Database Check: you run our read-only script in your SQL editor and send us the results. We need no passwords, no keys and no login to your account, and we change nothing. A person goes through every table and storage bucket, and you get a plain red, yellow and green report with the order to fix things in. Get the $49 Database Check

A person reviews and approves every report. It's not a penetration test or a security guarantee.

Sources