What is Supabase Row Level Security, and how do you know if it's misconfigured?
Security basics · 6 min read
Why Postgres alone isn't enough
Supabase is Postgres exposed directly over a REST API (PostgREST), reachable from the browser using the public anon key. That's normal and expected, but it means there's no backend server in the middle deciding what a request is allowed to touch. That job falls entirely to Row Level Security (RLS), a Postgres feature that lets you write a policy per table saying which rows a given request can read or write.
The two ways it goes wrong
RLS is off entirely. Tables created without RLS enabled are readable and writable by anyone with the anon key, which, since that key is meant to be public, means anyone with a browser and your site's URL.
RLS is on, but the policy doesn't actually restrict anything. A common pattern during development is a policy like USING (true) ("allow this operation, no conditions") meant as a temporary placeholder that never gets tightened before shipping. RLS is technically enabled, the dashboard shows a green checkmark, and every row is still fully public.
How to test it yourself
Find your project's anon key (it's meant to be public, so it's safe to use for this) and your project URL, then query a table directly:
curl "https://YOUR_PROJECT.supabase.co/rest/v1/your_table?select=*&limit=1" -H "apikey: YOUR_ANON_KEY"
If that returns real rows for a table that should be private (user profiles, orders, anything not meant to be world-readable), RLS is either off or not actually restricting access.
How Auditly checks this
This is the one check in Auditly that's opt-in rather than automatic, because it makes a real request against your live database rather than just reading public information. When enabled, Auditly discovers your Supabase project URL and anon key from your client-side JS (the same way it finds exposed secrets), then makes a single read-only, limited request against each discoverable table to check whether it returns data. The report only ever states how many rows/columns were exposed, never the actual row content, so testing for the problem never becomes another way to leak the data.