Supabase RLS checker

Row Level Security (RLS) is what stops strangers from reading your Supabase tables. This tool runs one outside test: it asks each table your database lists for one row, as a logged-out visitor, and reports whether rows come back. It does not test signed-in users, other tenants, writes or every policy.

Check your Supabase tables

Works with any app that talks to Supabase (Lovable, Bolt, v0, Cursor, and hand-built apps). Read-only.

What it means

Anyone can read this table without logging in. That can be deliberate, such as reference data or a public directory, or a missing access policy; only you can say which. xlogs read one row's column names and kept none of its values.

Why AI tools cause it

Supabase only limits who can read a table once you turn on Row Level Security (RLS) and write access rules for it. An AI tool can build a working app without doing that, and a table without RLS, or with a policy that lets everyone read, answers anonymous requests.

How xlogs checks it

xlogs finds your public Supabase URL and anon key in your app (the same ones your frontend uses), asks the database for its table list, reads one row from each listed table as an anonymous user, and flags any that return rows. Hosted Supabase refuses that list to public keys, so on a hosted project the result says the tables could not be checked. Read-only: it never writes or deletes.

Why xlogs runs this check on your app

  1. We observeSupabaseA Supabase URL or key in the served code
  2. So we run a check we otherwise would notFinding a Supabase connection is what makes the database check run at all. Without it there is no database for us to ask anything of.
  3. And you geta result with the evidence behind it, fix instructions for your coding assistant, and a way to confirm it worked

Nothing about Supabase is a problem by itself. Observing it is what makes this check relevant; the result comes from the check, not from the detection.

What this checker does

It finds the public Supabase URL and anon key your app already ships and asks your database for its table list. Hosted Supabase has refused that list to public keys since April 2026, so on a hosted project this check cannot name a table, and it says so. Where the list answers, each listed table gets one read as a logged-out user (at most 25); a table that returns rows can be read by anyone with your public key, and only you can say whether that is intended.

It reports the table name, its column names, and an approximate row count as proof. It never stores the row contents, because the point is to warn you, not to copy your data.

How to fix it

The goal: Row Level Security is enabled on every table, and each table returns rows only to the users it is meant for: a private table answers no anonymous request, and a table that is public on purpose has a read policy that says so.

  1. Decide whether the affected spot is meant to be readable without a login
  2. If it is not, enable Row Level Security (RLS) on the affected spot and add a policy so only the users allowed to see a row can read it
  3. If it is, keep it public on purpose, and record that decision so a review does not reopen it
  4. Do not switch on a restrictive policy for a table your public pages read without a replacement read policy, or those pages stop working
  5. Re-check that an anonymous request returns what you decided it should
Paste this to your AI coding tool (Lovable, Cursor, Claude Code):
In my app (the spot the scan shows): anyone can read this table; confirm that is deliberate. Please make this true: Row Level Security is enabled on every table, and each table returns rows only to the users it is meant for: a private table answers no anonymous request, and a table that is public on purpose has a read policy that says so. Steps: Decide whether the spot the scan shows is meant to be readable without a login; If it is not, enable Row Level Security (RLS) on the spot the scan shows and add a policy so only the users allowed to see a row can read it; If it is, keep it public on purpose, and record that decision so a review does not reopen it; Do not switch on a restrictive policy for a table your public pages read without a replacement read policy, or those pages stop working; Re-check that an anonymous request returns what you decided it should. Then tell me exactly what you changed, and do not print any secret values back to me or write one into a commit message, a pull request description or a log.

Then verify: After you decide and deploy, xlogs repeats the same anonymous read of each table the database lists and reports what comes back: a table you made private should no longer return rows, and one you keep public still will. If the table list is refused, it says the tables could not be checked.

Common questions

What is RLS in Supabase?

Row Level Security is a Postgres feature that decides, row by row, who is allowed to read or change data. In Supabase you turn it on per table and add policies (for example, a user can read only their own rows). Until you do, a table with RLS off is readable by anyone with the public key.

Will enabling RLS lock my own app out of its data?

Only if you enable it without policies, so add the policies at the same time. A common pattern is: enable RLS, then add a policy allowing authenticated users to read their own rows, and keep any genuinely public tables public on purpose. Then re-run this check to confirm private tables no longer leak.

Can I check RLS without giving you credentials?

Yes. The checker only uses the public URL and anon key your app already ships to every browser. You never hand over a password or a service key.