Supabase RLS checker: is Row Level Security actually protecting your table?

Someone told you to “turn on RLS.” You did. The Supabase dashboard shows a green toggle next to every table. That toggle tells you almost nothing about whether a stranger can actually read your customers’ data.

in plain language

What Row Level Security actually is

Every table in your Supabase database is really a Postgres table, and by default, Postgres doesn’t care who’s asking — a query either matches the table or it doesn’t. Row Level Security (RLS) is a set of rules you attach to a table that say, in effect, “only let this row through if the person asking is allowed to see it.” Turning RLS on for a table without writing any rules doesn’t protect it — it does the opposite: with no rule written, the default is to deny every row, which usually gets “fixed” by a permissive rule that quietly defeats the point.

The reason this matters more for a Supabase app than a typical backend: your browser already has the credentials to talk to the database directly (the public anon key, sitting in your JavaScript bundle on purpose). RLS is the only thing standing between that key and every row in every table it can reach.

the failure modes

Three ways RLS fails while still showing “enabled”

All three pass a glance at the dashboard. None of them survive someone actually trying to read a row that isn’t theirs.

  • A policy that reads using (true). This is the most common one — it was written to stop a “permission denied” error while building, and never got tightened. It technically satisfies “a policy exists,” and it lets every row through to anyone.
  • A SELECT policy with no matching policy on UPDATE. Someone locks down who can read a row and forgets that Postgres checks WITH CHECK separately for writes. The result: users can’t see each other’s rows, but they can still edit them if they can guess or enumerate the row’s id.
  • A table nobody wrote a policy for at all. A second or third table added later — a `notifications` table, an `invites` table — that never got the same treatment as the first one. It’s the easiest one to miss because it’s an absence, not a mistake you can see.
do it yourself

How to check it in the Supabase dashboard, in about 10 minutes

This is a genuinely useful manual pass — do it whether or not you ever run an automated check.

  1. 01
    Open Table Editor and look for the shield iconEvery table shows whether RLS is enabled. Note every table where it isn't — that's an instant finding, no further digging needed.
  2. 02
    Open Authentication → Policies for each table that has data you don't want publicRead every policy’s USING expression. If it says true, or references a column that’s always the same for every row, the policy isn’t doing anything.
  3. 03
    Check that INSERT and UPDATE have their own WITH CHECK, not just SELECT rulesA table can look fully locked down and still let any signed-in user overwrite someone else's row on the write path.
  4. 04
    Actually try it with the anon keyOpen your browser's dev tools on your live app, copy the request your app makes to Supabase, and change an id in it. If a row that isn't yours comes back, RLS isn't protecting that table — full stop, no more reading required.
what Solacess adds

D1 and D2: the same test, run against every table, automatically

The manual pass above works, but it doesn’t scale past a handful of tables, and it’s easy to skip the one table that matters. Solacess’s Supabase probe runs the equivalent check against every table your app’s own anon key can see — no service-role key, no database password, just the same key your browser already has.

D1RLS disabled on a table entirely — listed and read back with the anon key, proving the table is open, not just flagged as unprotected.
D2RLS enabled but wrong — a using(true) policy, an inverted check, or an UPDATE with no matching WITH CHECK, each one reproduced against the live table.
verified vs suspected
A D1 or D2 finding is only marked verified when the check actually read or wrote a row it shouldn’t have been able to. If the anon key is blocked the way it should be, nothing is reported for that table — Solacess doesn’t flag a policy for existing, only for failing.

See what an open table actually looks like, proved.

The sample report is a real scan of a deliberately vulnerable app — nine findings, the proof behind each one, and what they would cost.