How to test Supabase RLS from the outside and inside

Start by checking what a logged-out visitor can read from your deployed application. Then test your intended permissions with two separate accounts and database tests. These checks answer different questions: an external read test alone cannot validate every RLS policy.

Webba Creative Technologies · Reviewed 2026-09-11

Check your application

Write down who should see each kind of data

Before running a tool, pick a concrete resource such as a profile, an order or a published article. Decide whether it should be public, private to its owner, or shared within an organisation. A readable public catalogue is not automatically a vulnerability. A readable private record is a different result.

Build the test around synthetic records whose owners you know. Include at least two accounts with different ownership, plus a visitor without a login. Keep administrative credentials out of these checks: privileged access can hide the very restriction you are trying to verify.

IdentityExpected accessCheck separately
Visitor without a loginOnly intentionally public rowsPrivate rows remain inaccessible
Account ARows belonging to ARows belonging to B remain inaccessible
Account BRows belonging to BReads and writes follow the intended rules

Check the deployed public application

Enter the public HTTPS application URL in RLS Checker. Use an application you own or are authorised to assess. The checker looks for Supabase configuration and observable table references in public client resources, then makes bounded anonymous read requests.

Review both the result and the coverage. A rejected read means that request was blocked. An empty response is inconclusive. If the checker cannot discover your configuration or a table, the lack of a finding does not establish that the table is protected.

This outside view can catch a deployment that differs from your local settings. It does not inspect your SQL policy definitions or test logged-in users, writes, Storage, or every API route. Continue with project-level tests even when the external result looks reassuring.

Test account boundaries and each operation

On a local or staging project, sign in as account A and request a known record owned by B through the same API your application uses. Repeat in the other direction. Also test a legitimate record so that a broken application is not mistaken for successful isolation.

Exercise SELECT, INSERT, UPDATE and DELETE separately where your app supports them. A SELECT policy does not prove that writes are safe. Define expected row ownership before attempting a write, and use disposable test records instead of production customer data.

Supabase combines schema and table grants with row policies. Inspect both layers. Run automated database tests as the roles being tested, not as a table owner, superuser or role that bypasses RLS. Use the official Supabase test tooling for repeatable regression checks.

Reproduce why a zero-row result is ambiguous

Our downloadable SQL fixture creates five synthetic cases in a disposable local database. It uses a dedicated visitor role without BYPASSRLS, checks visible rows, verifies a permission rejection and rolls back every created object.

The filtered table and the empty table both return zero rows. One contains a row protected by RLS; the other has no RLS and no data. This is why a single empty external read cannot certify your policies. The fixture tests PostgreSQL behaviour, not the Supabase HTTP gateway or the full VICE scanner.

Local fixtureVisible rowsWhat it shows
exposed1SELECT granted, RLS disabled: the visitor can read the row.
public_catalog1RLS enabled with an intentional public read policy.
filtered0A row exists, but RLS hides it from the visitor.
empty_table0No row exists, even though RLS is disabled.
deniedError 42501The visitor has no SELECT grant.

Retest after the deployed permissions change

After fixing a policy or grant, rerun the account tests and the external check. Confirm that legitimate access still works and that the previously exposed private record is no longer available to the unintended role. Preserve a synthetic regression case for future migrations.

If you need authenticated coverage across your application, the VICE documentation describes the broader audit workflow. Keep the result of this free checker scoped to anonymous Supabase reads; it is not a security certification.

Reproduce the five local cases

Run only in a disposable local PostgreSQL database. The script creates synthetic data in a transaction, verifies permissions and rolls back its changes. It does not connect to a Supabase project.

Download the SQL fixture

Continue your checks

Supabase returns an empty array: is RLS working?

An empty Supabase response can mean filtered rows, an empty table or a query mismatch. Reproduce the distinction and test permissions correctly.

Read the guide

Is an exposed Supabase anon key a security problem?

Learn which Supabase keys belong in a browser, which must stay secret, and how to check the data access available with a public key.

Read the guide

How RLS Checker tests anonymous Supabase access

Read the RLS Checker methodology: discovery, bounded anonymous reads, evidence, report limits and a reproducible PostgreSQL fixture.

Read the guide