← BlogHow to check3 min readUpdated 2026-08-03

How to check if your Supabase RLS actually works

Checking that a policy exists is not the same as checking that it denies anything. Test it the way an attacker would.


Supabase gives your browser a key and lets it query the database directly. Row Level Security is what decides which rows come back. Get it wrong and the answer is: all of them, to anyone.

The two-minute test

Query your own database as a stranger

  1. 1Open your live app, press F12, Network tab, reload the page.
  2. 2Click any request to *.supabase.co and copy the apikey header value and the project URL.
  3. 3List every table the key can see by requesting /rest/v1/ — Supabase returns an OpenAPI document naming them.
  4. 4For each interesting table, run the query below.

List the tables, then read one

# what tables can this key see?
curl -s "https://YOUR-PROJECT.supabase.co/rest/v1/" \
  -H "apikey: YOUR_ANON_KEY" | head -c 2000

# can an anonymous caller read rows?
curl "https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*&limit=3" \
  -H "apikey: YOUR_ANON_KEY" \
  -H "Authorization: Bearer YOUR_ANON_KEY"

Reading the result

  • [] — the table is protected. RLS is on and no policy grants anonymous reads.
  • Rows of real data — the table is public to the internet. Fix before anything else.
  • {"message":"permission denied for table ..."} — RLS is on and denying at the role level. Also fine.
  • Something about the relation not existing — wrong table name, not a security result.

Common ways RLS ends up not working

  • RLS was never enabled on the table — policies are ignored entirely until you ALTER TABLE ... ENABLE ROW LEVEL SECURITY.
  • A policy with USING (true), often added to 'make it work' during development and never revisited.
  • A policy on SELECT only, leaving INSERT, UPDATE and DELETE unrestricted.
  • Rules based on a role stored in user metadata the client can edit — the user can promote themselves.
  • Views that bypass the underlying table's policies.
Paste this into your AI coding tool
Audit Row Level Security across my whole Supabase project.

For every table in the public schema:
1. Report whether RLS is enabled.
2. List its policies, with the full USING and WITH CHECK clauses.
3. Flag any policy that is unconditional (USING true) or that depends on a
   value the client controls, such as a role stored in editable user
   metadata.
4. Confirm coverage for all four commands — SELECT, INSERT, UPDATE,
   DELETE — not just SELECT.

Then fix them: enable RLS everywhere, replace unconditional policies with
ownership checks written as USING ((select auth.uid()) = user_id), and for
tables no browser should touch, enable RLS with no policy so they deny by
default.

Finish with a table: table name, RLS on/off, policies per command, and
whether an anonymous user can read it.

This is one check out of 40+

Paste your site address and we run the whole list from the outside — leaked keys, open databases, unprotected pages — then hand you one prompt that fixes what we find. Free, about 30 seconds, no signup.

Check my site — free