Audit Supabase RLS: policies, roles and tenant isolation
Timo Wevelsiep•Updated: 25.08.2026Editorial note: Versions, commands and prices may change. Please verify critical steps independently before production use. This guide does not replace individual consulting.
Need to test RLS together with Auth, Storage and real user journeys? WZ-IT provides managed Supabase, migration and security reviews for production applications.
To audit Supabase RLS, two layers need to be read together: PostgreSQL privileges determine whether a role may perform an operation at all. Row Level Security then determines which rows the operation may affect.
Defects commonly appear not within one policy, but at the boundaries between grants, roles, Auth claims, views, functions, Storage and server-side keys.
What RLS protects in Supabase
Supabase exposes database tables through generated APIs. For exposed tables, RLS is the central data-access control. Common rules include:
- users may read only their own profile
- tenants see only their own records
- roles may change selected status values
- anonymous users see published content only
- users may access files only in their own path
RLS does not replace authentication. Auth provides identity and claims; RLS consumes those inputs in database policies.
1. Inventory the data and attack surface
An audit starts with every reachable data path, not the first policy:
| Data path | Questions |
|---|---|
| Tables | Which schemas are exposed through APIs? Is RLS enabled? |
| Views | Under which role does the query execute? |
| RPCs | Which functions are callable and with which rights? |
| Storage | Which buckets, paths and policies exist? |
| Realtime | Which tables and events are published? |
| Edge Functions | Which keys and user tokens do they use? |
| Backend | Where is service_role or a database role used? |
A table not referenced directly by the frontend can still be reachable through generic APIs when schemas, grants and roles allow it.
2. Separate roles and keys
Common roles and contexts include:
- anon for unauthenticated client requests
- authenticated for signed-in users
- service_role for controlled server-side administration
- database roles for internal processes or direct connections
The publishable key is not a secret. Its safety depends on the associated role having limited privileges and RLS applying. A service key is highly privileged and must never appear in a browser, mobile bundle or publicly readable repository.
3. Read RLS state and grants together
For each exposed table, check:
- Is RLS enabled?
- Is forced RLS needed for relevant owner access?
- Which rights do anon and authenticated receive?
- Are legacy or broad grants present?
- Does every required operation have an intentional policy?
- Do administrative processes use a bypass role?
Broad grants are not automatically repaired for every operation by a good SELECT policy. Conversely, a policy without the required table privilege leaves the application non-functional.
4. Use USING and WITH CHECK correctly
Policies distinguish existing and new row states:
- USING filters rows that SELECT, UPDATE or DELETE may see or affect.
- WITH CHECK validates new values for INSERT and UPDATE.
A common UPDATE flaw permits reading an owned row but does not prevent changing its tenant identifier. Write operations need both current access and the proposed state to be evaluated.
5. Auth claims and null cases
Supabase documentation points out that the Auth user identifier is null for unauthenticated requests. Policies should express the intended authentication state explicitly.
Review:
- behaviour without a login
- expired or invalid tokens
- email and role claims
- stale claims after role changes
- user metadata the user may change
- tenant switching or multiple memberships
Security decisions must not rely on freely editable client data.
6. Test tenant isolation as a business rule
One test account cannot verify a multi-tenant system. A minimal matrix uses:
- user A in tenant A
- user B in tenant A with another role
- user C in tenant B
- anonymous access
- a server-side process
Test:
| Operation | Positive test | Negative test |
|---|---|---|
| SELECT | own row visible | other tenant row invisible |
| INSERT | own tenant accepted | manipulated tenant ID rejected |
| UPDATE | allowed fields change | owner or role escalation blocked |
| DELETE | authorised role deletes | regular user and other tenant fail |
Negative cases reveal more than a generic “the API works” check.
7. Control views and functions
Views and database functions may run with rights different from the caller. Review:
- ownership and execution mode
- search path and schema resolution
- executable roles
- dynamic SQL
- input validation
- access to RLS-protected tables
- unintended aggregation of data
Security-definer functions are not inherently wrong. They need a narrow purpose, fixed search path, minimum privileges and targeted tests.
8. Include Storage policies
Supabase Storage uses tables and RLS policies for object access. Audit:
- public and private buckets
- path structure and ownership logic
- upload, read, update and delete independently
- content-type and size restrictions
- signed URLs
- server-side uploads
- orphaned metadata or objects
Secure application tables do not automatically make files secure.
9. Edge Functions and backends
Server-side components can pass a user token and inherit the same RLS. When they use service_role, they own authorisation themselves.
Ask:
- Is service_role genuinely required?
- Is user identity verified before an administrative action?
- Can the client manipulate object or tenant IDs?
- Are secrets logged?
- Are webhooks authenticated and safe to retry?
10. Repeatable RLS review gates
A one-time audit loses value with every schema change. Put RLS tests into migrations and releases:
- do not expose a new table by default
- grant privileges deliberately
- enable RLS and version policies
- automate role and tenant tests
- run negative cases on every change
- inventory service_role use
- record findings with severity and owner
Sources
Rather have it operated?
You'd rather not run Supabase yourself? WZ-IT handles setup, operations and maintenance - privacy-focused from Germany.
Enquiry
Migrate, secure, or operate Supabase
We migrate Supabase projects and existing backends such as Firebase, Lovable, PostgreSQL, MySQL or Auth0, build the target environment, and can take over ongoing operations.
Frequently Asked Questions
Answers to the most important questions
RLS is a PostgreSQL feature that controls row access using policies and the current role or session. Supabase uses it to constrain browser and app access to exposed tables.
No. Without matching policies, access is generally blocked, but grants, roles, views, functions and Storage also need to be correct. Owners and service roles may behave differently or bypass RLS.
USING defines which existing rows an operation may see or affect. WITH CHECK defines which new row states are allowed during INSERT or UPDATE.
A successful request proves only the permitted path. Tests with another tenant, manipulated IDs, no authentication and forbidden operations demonstrate whether policies actually isolate data.
No. Service keys can bypass RLS and belong only in controlled server-side environments. Shipping one in browser or app code defeats policy protection.
More on Supabase
- What is Supabase?
- Supabase Cloud vs self-hosted
- Supabase self-hosting: pros & cons
- Supabase cost: Cloud vs self-hosted
- Migrate Supabase Cloud to self-hosted
- Migrate a Supabase project, region or organisation
- Migrate PostgreSQL to Supabase
- Migrate AWS RDS and Aurora to Supabase
- Migrate Heroku Postgres to Supabase
- Migrate Neon and Vercel Postgres to Supabase
- Migrate MySQL and MariaDB to Supabase
- Migrate Microsoft SQL Server and Azure SQL to Supabase
- Migrate Firebase to Supabase
- Migrate Lovable Cloud to Supabase
- Migrate Auth0 to Supabase Auth
- Supabase migration checklist
- Back up Supabase completely
- Update self-hosted Supabase
- Audit Supabase RLS
- Run Supabase on Hetzner
- Install Supabase with Coolify





