Is your Supabase data public? How to check Row Level Security

Updated 27 October 2026 · By Tidyship · 6 min read

Your Supabase data is effectively public if any table in the public schema has Row Level Security (RLS) turned off, or has a rule that lets everyone read it, such as using (true). The publishable (anon) key in your website is meant to be public, so RLS is the only thing stopping a stranger from using it to read or change your tables. To check, run the Security Advisor and the two SQL queries below. It takes about 5 minutes.

Why this matters for Lovable and Bolt apps

Apps built with Lovable, Bolt and similar tools talk to Supabase straight from the browser. Supabase exposes the tables in your public schema through its API, and the key your website uses lets anyone call that API. RLS rules decide which rows each caller can see and change.

This isn't theoretical. A 2025 scan of 1,645 apps from Lovable's showcase found 170 where tables could be read or written with just the public key (CVE-2025-48757).

Step 1: Run the Security Advisor

In the Supabase dashboard, open Advisors → Security Advisor. Act on these warnings first:

  • Table publicly accessible: RLS is off, so anyone can probably read and change the table.
  • Security policy not enforced: you wrote rules, but RLS is switched off, so they do nothing.
  • Security policy allows unrestricted access: a rule like using (true) that lets everyone in.
  • View bypasses row-level security: a view that skips the rules of the tables behind it.
  • Security policy relies on user-editable data: a rule that trusts something users can change about themselves.

The Advisor catches rules that are switched off or open to everyone. It can't know whether a rule is right for your data, for example whether every signed-in user should see every order. That's step 3.

Step 2: List every table and whether RLS is on

Open the SQL Editor and run:

select tablename, rowsecurity as rls_on
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;

Any table with rls_on = false is probably open to anyone who has your public key. That's everyone who has visited your site.

Step 3: Read your rules

Now list every policy:

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

Look for these red flags:

If you seeIt means
using_rule is true on a SELECT ruleEveryone can read every row. That's fine for public content like blog posts. It isn't fine for users, orders, bookings or messages.
roles includes anon or public on a table with personal dataSigned-out visitors are let in.
A rule that only checks auth.role() = 'authenticated' or auth.uid() is not nullAny signed-in user can see every user's rows. Signing up is free, so this is nearly as open as true.
A rule that reads user_metadata, for example to decide who's an adminUsers can edit their own user_metadata, so they can give themselves that role. Keep roles in a table users can't change.
Users can UPDATE their own row in a table with columns like role, is_admin, is_pro, plan or creditsUsers can make themselves admin or give themselves Pro. RLS controls which rows they can change, not which columns.

Step 4: Test it like a real user

Create two test accounts. Add something private as account A (an order, a booking, a message). Sign in as account B and look for it everywhere in the app. Then sign out and check that pages showing personal data stay empty.

Don't forget views and storage

  • Views run with their creator's permissions by default, so they skip RLS. On Postgres 15 or later, make them follow the caller's rules with alter view public.my_view set (security_invoker = true);.
  • Storage buckets marked Public let anyone with a file's URL download it. Check that no public bucket holds private files such as ID photos, invoices or contracts.

How to fix it

Fix prompt for Lovable or Bolt

"Enable Row Level Security on every table in the public schema. Add policies so each user can only select, insert, update and delete their own rows (where user_id equals auth.uid()). Don't use 'using (true)' or 'auth.role() = authenticated' on any table that holds personal data. Users must not be able to change role, is_admin, is_pro, plan or credits columns. List every table and policy you changed."

If a developer is fixing it, here is the usual pattern for a table where each row belongs to one user:

alter table public.orders enable row level security;

create policy "Users read own orders" on public.orders
  for select to authenticated using ((select auth.uid()) = user_id);
create policy "Users add own orders" on public.orders
  for insert to authenticated with check ((select auth.uid()) = user_id);
create policy "Users change own orders" on public.orders
  for update to authenticated
  using ((select auth.uid()) = user_id) with check ((select auth.uid()) = user_id);
create policy "Users delete own orders" on public.orders
  for delete to authenticated using ((select auth.uid()) = user_id);

To stop users changing sensitive columns on their own profile, limit which columns they can update:

revoke update on public.profiles from anon, authenticated;
grant update (full_name, avatar_url) on public.profiles to authenticated;

Turning RLS on with no rules blocks the table completely, so add the rules in the same change and test the app straight after. Then run steps 1 to 4 again.

Questions

Is it safe that my Supabase anon key is visible in the browser?

Yes, as long as Row Level Security is set up correctly. The anon (publishable) key is designed to be public. RLS decides what each person using it can see.

I turned on RLS and now my app shows no data. Why?

With RLS on and no policies, the table refuses every request through the API. Add policies for the reads and writes your app needs, in the same change.

Does the service_role key respect RLS?

No. The service_role key, and the newer secret key, skip RLS completely. That's why they must never be in your website. See how to check for an exposed key.

Lovable's security scan passed. Am I safe?

Not necessarily. A policy can exist and still be too open. For example, a rule that only checks the user is signed in lets every user read everyone's rows. Check your policies with the second query in this guide.

Sources