Home / Guides

Guide  /  4 min read  /  Checked Oct 2026

Supabase RLS for beginners: lock your tables in plain English

Row Level Security decides who can read and change each row in your Supabase database. Here is what it does, how to turn it on, and how to test it, in plain English.

If your AI built your app on Supabase, there is one setting that matters more than almost anything else: Row Level Security, usually shortened to RLS. This guide explains Supabase RLS for beginners, with no database background needed. By the end you will know what it does, how to check it, and how to test that it actually works.

What RLS does, in one paragraph

Your app's front end talks to Supabase directly from the visitor's browser, using a public key. That key is meant to be public (we explain why in our guide to the Supabase anon key). So the database itself has to decide who is allowed to see and change which rows. RLS is how it decides. Supabase's docs describe a policy as adding a hidden "WHERE" filter to every query: "only show rows where the owner is the person asking".

Why it matters so much

Supabase's own documentation puts it bluntly: a table in an exposed schema without RLS is readable and writable by any role with a grant on it. In plain English, if RLS is off on a table in the public schema, anyone who opens your site and looks at the network traffic may be able to read or change that table.

Two more facts from the docs worth knowing:

  • Turning RLS on with no policies locks the table. Once RLS is enabled, no data is reachable through the API with the public key until you add policies. If your app suddenly shows nothing after you enable RLS, that's why. It's the safe kind of broken.
  • Policies and grants are separate. Grants decide whether a role can do an operation at all; policies decide which rows. On many projects, new tables come with broad grants already in place, and adding policies does not take those back.

Step 1: find the tables that are open

In your Supabase dashboard, open the Advisors section and run the Security Advisor. It flags tables in the public schema with RLS disabled. You can also open the Table Editor: tables without RLS are marked.

Write down every table it lists. Each one needs a decision: who should read it, and who should change it?

Step 2: turn RLS on

For each table, you can ask your AI to do this, but read what it writes before you run it. The basic statement looks like this (with your table name instead of profiles):

alter table public.profiles enable row level security;

Step 3: add owner-only policies

The most common pattern in a beginner app is "each user sees and edits only their own rows". It only works if the table has a column storing the owner's user id, usually called user_id. These examples follow Supabase's docs, one policy per operation:

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

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

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

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

What the pieces mean:

  • to authenticated means "only for signed-in users". Visitors who are not signed in get nothing from these policies.
  • using (...) decides which existing rows a user can touch.
  • with check (...) decides what a new or changed row is allowed to look like, so a user can't create a row that claims to belong to someone else.

The trap: using (true)

When an app breaks after RLS is turned on, AI tools sometimes "fix" it with a policy like using (true). That means "every row, for everyone the policy applies to". If it says to anon, every visitor who isn't signed in can read every row. Supabase's docs say to use that only for data that is meant to be public, like published blog posts.

If you see using (true) on a table holding user data, ask your AI why, in plain English, before keeping it.

Step 4: test it with two accounts

You don't need to write automated tests to get real confidence. Do this:

  1. Sign up for your app with two different email accounts, A and B, in two different browsers (or one normal and one private window).
  2. As A, create some data: a note, a profile, an order.
  3. As B, try to see it. Open every page where A's data might show up.
  4. Sign out completely and check again as a visitor.

If B or a signed-out visitor can see A's data, a policy is wrong. Supabase also supports proper database tests (supabase test db) when you're ready for that.

A prompt to hand your AI

List every table in my Supabase public schema and tell me, for each one,
whether RLS is enabled and what policies exist. Explain each policy in
one plain-English sentence. Do not change anything yet. Flag any table
with RLS off and any policy using (true).

Get the safety rules ready-made

Our free starter pack includes a rule that tells your AI never to turn RLS off or add a wide-open policy without explaining it first. The Vibe Coder Prompt Pack has a full set of copy-paste security prompts for Supabase, and the Ship-Safe Starter Kit includes a pre-deploy checklist with database checks like these.

Vibe Shipped is independent and not affiliated with Supabase. Written with AI assistance and reviewed by a human.

Want these rules ready-made?

The free starter pack has them, set up for Cursor and Claude Code.

Get the free pack