RLS is the most important switch in your Supabase project. Here is what it does, how to set it properly and which policies you usually need.
What row level security does
Your Supabase database is reachable straight from the browser through a public key. Convenient, but it means the database itself has to decide who may see which row. That is row level security: a rule per row saying whether the current user is allowed in.
With RLS off there is no brake: anyone holding your public key reads the whole table. That key sits in your frontend code and is visible to everyone.
RLS on without a policy means: closed
The moment you enable RLS, the database blocks everything no policy allows — including your own app. That is confusing: the app looks broken while security is actually working. The fix is not turning RLS off, but adding the right policies.
The policies you usually need
For a table holding users' own data it usually comes down to four rules: select, insert, update and delete, all tied to the signed-in user through auth.uid().
- Select: only rows where user_id equals auth.uid()
- Insert: only when the new row has user_id = auth.uid()
- Update and delete: same condition as select
- Public tables (like a blog index): read for everyone, never write
The pitfalls
- A policy using 'using (true)' — that is not security, that is an open door.
- Roles like 'admin' in the profile table the user can edit; someone makes themselves admin.
- The service role key in the frontend; it ignores every policy.
- Forgotten grants: a new table is not automatically reachable through the API, even with correct policies.
How to check it
Walk through every table: is RLS on, and is there a policy per action tied to the signed-in user? Then test with a second account whether you really cannot see the first user's data. That is the only test that counts.
Also use the Supabase security checklist to review the other settings.
Frequently asked questions
Can I switch RLS off while building?
You can, but it is risky: this is exactly what people forget to switch back. Work with policies and a test account from the start.
Is the anon key a secret?
No, it is meant to be public. It is only safe when your RLS and policies are in order.
Do I need policies for every table?
For every table reachable through the API, yes. Tables used only server-side can be closed off entirely.