Guides · 17 October 2026
Is my Supabase data public? How to check in 10 minutes
Apps built with Lovable, Bolt and similar tools talk to Supabase straight from the browser. That's normal, but it means one setting decides whether a stranger can read your users' data.
Why this matters
Your app's Supabase address and public (anon) key are shipped to every visitor's browser. That's how Supabase is designed, and it's fine. What stops someone using that key to read your tables is row-level security (RLS): rules, per table, saying who may read or change which rows.
If a table has RLS off, or has a rule that lets everyone through, anyone with your public key can read it. They don't need to log in, and they don't need your app.
Check 1: which tables have RLS off
- In the Supabase dashboard, open the Table Editor. Tables without RLS are labelled as unrestricted.
- Open the Security Advisor (under Advisors). It lists tables in the
publicschema with RLS disabled. - Or run this in the SQL Editor. Any row with
rowsecurity = falseis open:
select tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;Check 2: rules that let everyone in
RLS switched on isn't enough. A rule whose condition is simply true lets every request through, which AI tools often write to make an error go away. List your rules:
select tablename, policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'public'
order by tablename;qualorwith_checkoftruemeans no restriction for that action.rolesof{public}or{anon}means it applies to visitors who aren't logged in.- For a table of personal data (profiles, bookings, orders, messages), a
truerule forselectis almost always a leak.
Check 3: test it like an outsider
Only on your own project: copy your project URL and anon key from Project Settings → API, then ask for a table without logging in. If rows come back, anyone can do the same.
curl "https://YOUR-PROJECT.supabase.co/rest/v1/bookings?select=*&limit=5" \
-H "apikey: YOUR_ANON_KEY" \
-H "Authorization: Bearer YOUR_ANON_KEY"An empty list [] means the rules blocked you. That's what you want for private tables.
The usual fix
Turn RLS on and write rules that match how the table is used. For a table where each row belongs to one user (with a user_id column):
alter table public.bookings enable row level security;
create policy "Users read their own bookings"
on public.bookings for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Users create their own bookings"
on public.bookings for insert
to authenticated
with check ((select auth.uid()) = user_id);Switching RLS on with no rules blocks everything, so the app suddenly shows empty lists. That's safe, but test every screen after the change. Staff or admin views need their own rules.
Two more things to look for
- The service role key. It bypasses every rule. It must never appear in front-end code. Search your project for
service_role; it belongs only in server code or edge functions. - Storage buckets. A bucket marked public serves every file to anyone with the link. Uploaded IDs, invoices or private photos belong in a private bucket.
If you'd rather someone else looked
I flag open tables and exposed keys whenever I diagnose an app, and fixing the rules is a normal fix item. I don't sign off on security as a whole, but I'll tell you plainly what I found.
More on fixing apps built with Lovable, Bolt, v0 and Cursor.