Is my Supabase database public? RLS, and how to check

Here's the uncomfortable test: open your app, view source, and copy the Supabase URL and anon key sitting right there in the page. Anyone can. The only question that matters is whether those two public values let a stranger read your entire database — and for a lot of AI-built apps, the answer is yes.
"Is my Supabase database public?" has a precise answer, and it comes down to one feature being on and configured correctly. Let's check yours.
Why the public key is supposed to be safe
The anon key is public by design. It's meant to be in the browser. Supabase's security model never relied on hiding it — it relies on Row Level Security, database rules that fence off which rows each request is allowed to see. RLS is the lock; the anon key is just a key everyone's allowed to hold.
Turn RLS off and the fence is gone. That public key now reads and writes every row in the table — every user's email, every private record — straight from anyone's browser. The app still works perfectly, which is exactly why nobody notices.
The trap: created outside the Table Editor
Supabase turns RLS on automatically only when you create a table through its Table Editor. Create one any other way — raw SQL, a migration, or an AI tool writing your schema — and RLS is off by default. Since AI builders write a lot of schema, this is the single most common way an app ships with public tables.
The subtler trap: RLS on, still public
This is the one that fools people who think they're safe. You can enable RLS and still be wide open if the policy allows everyone. A policy of USING (true) reads as "this rule matches every row" — which is the same as no protection at all.
Check every table properly
- Enable RLS on every table in the public schema. Assume anything not made in the Table Editor is off until you've confirmed otherwise.
- Read each policy and check it actually ties rows to a user —
auth.uid() = user_id— rather than letting everyone through. - Test as an anonymous user. Query the table with just the anon key and see what comes back. If you get rows you shouldn't, so does everyone else.
This prompt does the whole audit, including the USING(true) trap:
Check whether my Supabase database is publicly readable, and fix it. Be blunt. 1. List every table in the public schema and tell me, for each, whether Row Level Security is ENABLED. Flag every table with RLS off — those are readable/writable by anyone who has the anon key (which is public). 2. For every table with RLS ON, show me its policies and tell me plainly whether they actually restrict rows to the owning user (e.g. auth.uid() = user_id) or whether a policy is effectively "allow everyone" — including the USING(true) trap where RLS is on but the policy lets everybody through. 3. Point out any table created via SQL, a migration, or generated code rather than the Table Editor, since those don't get RLS enabled automatically. 4. For each problem table, give me the exact SQL to enable RLS and add a correct, restrictive policy. Assume an attacker already has my anon key and my project URL — because they do.
Supabase's Row Level Security guide has the policy syntax. This is the exact gap behind the 2025 Lovable disclosure, and it sits inside the broader how to secure a Supabase app checklist.
Lock it, then watch what you can't see
RLS is a fix you apply once and then can't observe — nothing in the running app tells you a later migration quietly added a table with RLS off. The database side is on you to audit; the outward-facing side is where continuous monitoring helps.
Tell Me When Down watches your app from outside — the certificate, the uptime, the endpoints — so once you've closed the RLS gap, a different kind of change doesn't slip past you unnoticed.
Close the public-database gap, then keep an eye on the rest.
The audit above tells you if your Supabase data is exposed today. Tell Me When Down watches everything on the outside after — SSL, uptime, endpoints — free, no card, so the next quiet change reaches you first.
spot something wrong or out of date? [email protected] — we'll fix it