What is RLS in Supabase? A beginner's guide

Short answer: RLS (Row Level Security) is a Postgres feature you turn on per table in Supabase. Once it's on, every request through Supabase's API is checked against rules you write, called policies, which decide which rows each user can read or change. If a table in an exposed schema like public has RLS off, any role that's been granted access to it can read and write all of it.

Last checked: Oct 17, 2026 · Source: Supabase docs: Row Level Security

Why RLS matters in a Supabase app

Many Supabase apps talk to the database straight from the browser using the public anon (publishable) key. That key is public by design, so anyone can find it in your app's code (here's why that's expected). What stops a stranger from reading your users' data isn't the key. It's RLS plus the permissions (grants) on each table.

If you built your app with Lovable or another AI builder, the same applies. RLS is a setting on your own Supabase project, and you, the builder, decide whether it's on and what the policies allow.

How RLS works, in plain English

Think of a policy as an automatic filter added to every query. If a policy says "users can see rows where user_id is their own ID," then when a signed-in user asks for all rows, Postgres quietly returns only theirs.

Two checks happen before anyone touches a table:

LayerWhat it decidesSet with
GrantsWhether a role can select, insert, update or delete on the table at allgrant / revoke
RLS policiesWhich rows that action applies toalter table ... enable row level security + create policy

Supabase recommends setting both for every table you expose. Adding policies does not take away grants, so a table with policies but broad grants can still allow more than you meant.

The roles you'll write policies for

How to enable RLS in Supabase (step by step)

Run these in your own project's SQL Editor, or better, in a migration so every environment gets the same setup. This example is for a todos table where each signed-in user manages only their own rows.

1. Turn on RLS.

alter table public.todos enable row level security;

Heads-up: once RLS is on, the public key can't read or write anything in that table until you add policies. If a feature suddenly shows empty results, that's usually why.

2. Set the grants to match your app. Remove what the public roles don't need, then grant back only what they do:

revoke all on table public.todos from anon, authenticated;
grant select, insert, update, delete on table public.todos to authenticated;

Whether new tables get automatic grants depends on your project's settings and on Supabase's October 30, 2026 change (our explainer). Setting grants yourself, as above, works either way.

3. Add one policy per action.

create policy "Users can view their own todos"
on public.todos for select
to authenticated
using ( (select auth.uid()) = user_id );

create policy "Users can create their own todos"
on public.todos for insert
to authenticated
with check ( (select auth.uid()) = user_id );

create policy "Users can update their own todos"
on public.todos for update
to authenticated
using ( (select auth.uid()) = user_id )
with check ( (select auth.uid()) = user_id );

create policy "Users can delete their own todos"
on public.todos for delete
to authenticated
using ( (select auth.uid()) = user_id );

4. Test it. Supabase recommends writing tests for each table's policies and running them with supabase test db (see Testing your database). Check both sides: that the owner can do what they should, and that another user can't.

Data that everyone should be able to read

For truly public data, like a list of blog posts, you can allow reads for everyone and keep writes off:

alter table public.posts enable row level security;
revoke all on table public.posts from anon, authenticated;
grant select on table public.posts to anon, authenticated;

create policy "Anyone can read posts"
on public.posts for select
to anon, authenticated
using ( true );

Only use using ( true ) for data that's meant to be public. On a table with personal data, it lets everyone read every row.

Beginner mistakes to avoid

Permission denied vs. empty results: a missing grant gives a 42501 permission denied error. A policy that matches no rows just returns nothing. If something fails, check the grants before you rewrite the policy.

How to check RLS in your own app

These checks run inside your own Supabase project. Only check projects you own or have permission to test.

1. Run the Security Advisor in your Supabase dashboard. It flags common problems such as tables in public with RLS off, policies that allow unrestricted access (like using ( true )), and views that bypass RLS.

2. See which tables have RLS on:

select tablename, rowsecurity as rls_enabled
from pg_tables
where schemaname = 'public'
order by tablename;

3. Read your policies:

select tablename, policyname, roles, cmd, qual, with_check
from pg_policies
where schemaname = 'public'
order by tablename;

Look for tables with no policies, policies with no role named, and true on private data.

4. Run your policy tests with supabase test db after every schema change.

Want a quick outside check too? Run a free Greenlight scan to check the basics (HTTPS, security headers, exposed keys). It's not a full security audit.

Run a free check →

Paid reports: coming soon.

FAQ

What is RLS in Supabase?
Row Level Security is a Postgres feature that Supabase uses to control which rows each user can read or change through the API. You turn it on per table and write policies for it.

Is RLS on by default in Supabase?
Don't assume it is. Check each table: run the Security Advisor or the pg_tables query above, and enable RLS on every table in an exposed schema such as public.

What happens when I enable RLS without policies?
The public (anon/publishable) key can no longer read or write that table. Your data is protected, but features that use that table will stop working until you add policies.

Do I need RLS if my app has a login?
Yes. A login tells Supabase who the user is. RLS decides which rows they can see. Without it, a signed-in user could reach other users' rows if the table is granted to signed-in users.

Does RLS apply to the service_role key?
No. The service_role (secret) key bypasses RLS, which is why it must stay on your server and never ship in your app.

What's the difference between grants and RLS policies?
Grants decide whether a role can use a table at all. Policies decide which rows. You need both, and adding policies doesn't remove existing grants.

Does Greenlight check my RLS policies?
No. Greenlight's free scan checks the basics from the outside (HTTPS, security headers, exposed keys). It doesn't log into your Supabase project or read your policies, so use the Security Advisor and the steps above for that.

Sources

Related on Greenlight: Is the Supabase anon key safe to expose? · Will Supabase's October 30 change break your app?

Greenlight is independent and not affiliated with or endorsed by Supabase or Lovable. Product names are used only to describe compatibility. This page is educational, not a security audit or a security guarantee.