anon and authenticated (with any auth.uid() you choose) from psql or the Studio SQL Editor, then run your reads and writes against the policy to confirm it accepts the right things and rejects the wrong things. For the policies themselves, see the RLS Cookbook. For the model that determines who gets which role, see RLS Model.
How PostgREST sets the session
When PostgREST receives a request, it does roughly this on the connection before running your query:SET LOCAL scopes the change to the current transaction, so wrap your testing in a BEGIN ... ROLLBACK block and you can iterate without polluting other connections.
Test as the anon role
ROLLBACK undoes the SET LOCAL ROLE and any rows you happened to insert that the policy let through. Use it religiously while iterating — without it, you’ll accidentally leave the session in an unexpected role and the next query will mislead you.
Test as a specific signed-in user
Test custom claims (for auth.jwt() policies)
If you wrote a policy that reads auth.jwt() ->> 'app_role', you set that claim in the same JSON blob:
Test as service_role
service_role bypasses RLS, so testing as it confirms the row exists at all (independent of policies):
service_role returns a row but authenticated doesn’t, the policy is doing its job. If service_role doesn’t return it either, the row doesn’t exist — you have a different bug.
A small harness for iteration
Put this in a SQL file and re-run it as you tweak policies:SET LOCAL every time.
Common things that aren’t what they seem
-
Querying
auth.usersdirectly returns rows because theauth.userstable is owned bysupabase_auth_admin, notauthenticated. Don’t be fooled — policies on tables you create do apply. -
RAISE NOTICE 'auth.uid is %', auth.uid();doesn’t fail if the role/claims aren’t set — it just printsNULL. If you’re getting unexpected RLS denials, add aSELECT auth.uid(), auth.role();at the top of your test block to confirm what the session thinks it is. -
SET ROLE(withoutLOCAL) persists across the session. If you accidentally use it and later wonder why your policies are denying everything, runRESET ROLEand you’ll be back to the connecting user. -
The Studio SQL Editor runs as
postgres(the project owner) by default. WithoutSET LOCAL ROLE, RLS is bypassed entirely — every query looks like it works. AlwaysSET LOCAL ROLEfirst.
Next steps
RLS Cookbook
The five core patterns this guide is for testing.
RLS Model
Roles, JWT claims, and how PostgREST sets them on real requests.