← Blog·Supabase

Supabase RLS Explained Without the Database PhD

Row-level security sounds intimidating. It isn't. Here's Supabase RLS in plain founder English, with the 3 policies that cover most real apps.

Massimo Di Chiaravi3ecoding Team·Jun 5, 2026·3 min read
Supabase RLS Explained Without the Database PhD

Most people hear "row-level security" and assume they need a database PhD.

You don't. You need about an hour and three policies you'll reuse forever.

RLS isn't database wizardry. It's a tiny set of rules that decide who is allowed to see and change each row. That's the whole concept.

Related reading from vi3ecoding:

I get asked about this every week, usually right after someone realises they shipped an app where any logged-in user could read every other user's data. Let's make sure that's not you.

What RLS actually is

Row-level security is Postgres saying: "Before I hand back this row, let me check if the person asking is allowed to see it."

Without RLS, every authenticated user can see every row in a table. That's fine for a public blog. It's catastrophic for anything with private data.

Supabase turns RLS on per table. Until you write policies, the table is locked. That default is a feature, not an annoyance.

The mental model: bouncer at the door

Stop thinking about RLS as "database stuff." Start thinking about it as a bouncer.

  • The bouncer reads the auth token of the person at the door.
  • The bouncer checks one rule per request: "Are you on the list for this row?"
  • If yes, you get in. If no, the row doesn't exist as far as you're concerned.

That's it. Every RLS policy you'll ever write is some version of that rule.

The 3 policies that cover most apps

After enough Vi3ecoding client projects, I noticed I was writing the same three patterns over and over. These three cover something like 80% of real-world apps.

Pattern 1 — "Users can only see their own rows"

For tables like profiles, notes, bookings.

create policy "users see their own"
on public.notes for select
to authenticated
using (auth.uid() = user_id);

One line. Done. Combine with insert/update/delete versions of the same and you're shipped.

Pattern 2 — "Members of an org can see org rows"

For multi-tenant apps. Each row has an org_id. A user belongs to one or more orgs through a join table.

create policy "org members can read"
on public.invoices for select
to authenticated
using (
  exists (
    select 1 from public.org_members
    where org_members.org_id = invoices.org_id
      and org_members.user_id = auth.uid()
  )
);

This is the pattern that runs foodla.eu — restaurants can read their own data, never each other's.

Pattern 3 — "Public read, owner write"

For things like public profiles, marketplace listings, blog comments.

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

create policy "owners can write"
on public.listings for update
to authenticated
using (auth.uid() = owner_id);

Two policies, two responsibilities. Cleaner than trying to overload one.

The mistake everyone makes once

Storing a role like is_admin directly on the profiles table, then writing an RLS policy that reads it.

Don't.

Roles live in their own table (user_roles), checked through a security-definer function. Otherwise a clever attacker flips their own admin flag and your whole RLS model collapses. This is one of those mistakes that's invisible until it isn't.

How I test RLS before shipping

A 5-minute ritual.

  1. Log in as User A. Create a row. Confirm you can see it.
  2. Log in as User B. Confirm you cannot see User A's row.
  3. Try to update User A's row as User B. Confirm it fails.
  4. Try the same as an anonymous visitor. Confirm it fails.

If any of those four checks doesn't behave, the policy is wrong. Don't ship until they all do.

When RLS feels too restrictive

It almost always means a missing table, not a missing policy.

If you're writing weird OR clauses to make one policy cover three cases, split the data. A separate shared_with join table is almost always cleaner than a clever single policy.

So — do you need a database PhD for Supabase RLS?

No.

You need three patterns, an hour of practice, and the habit of testing as two users before you ship.

RLS feels intimidating because it sits next to a lot of intimidating SQL. The thing itself is small. Treat it that way and it stops being scary.

Sources

On this page

About the author

Massimo Di Chiara — Founder of Vi3ecoding
Massimo Di Chiara

Founder of Vi3ecoding

Massimo Di Chiara is the founder of Vi3ecoding. After more than 100 web projects, he now explores how AI, ChatGPT and Vibe Coding help people turn ideas into real digital products.

Read Massimo's story
Website ready in 30 minutes

Your first website is closer than you think.

Start today. Build something real this week.

Start your first websiteNo coding. No experience required.