What is Row Level Security (RLS)? Supabase Guide

Share

TL;DR

Row Level Security (RLS) is a database feature that controls which rows each user can see or modify. Instead of filtering data in your app code, the database itself enforces access rules. In Supabase, RLS is critical because your anon key is public. Without RLS policies, anyone could read your entire database. Enable RLS on every table and write policies that restrict access to each user's own data.

The Simple Explanation

Imagine a shared Google Sheets document where everyone can only see rows they created. They don't even know other rows exist. That's what RLS does for your database.

Without RLS, your database is like a spreadsheet where everyone can see everything. With RLS, each user has their own filtered view.

Why RLS Matters for Supabase Apps

Supabase is designed to let you query the database directly from your frontend. The JavaScript client uses an "anon key" that anyone can see in your page source. This is intentional and safe, but only if you have RLS enabled.

Without RLS: Anyone with your anon key can run queries like SELECT * FROM users and get EVERYONE's data. This is the #1 security mistake in Supabase apps.

How RLS Works

RLS uses policies that act like automatic WHERE clauses. When a user queries the database, the policy adds conditions to filter the results.

Enable RLS and add policy

-- Enable RLS on the table ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- Users can only see their own posts CREATE POLICY "Users see own posts" ON posts FOR SELECT USING (auth.uid() = user_id);

-- Users can only insert as themselves CREATE POLICY "Users insert own posts" ON posts FOR INSERT WITH CHECK (auth.uid() = user_id);

Common RLS Patterns

Public Read, Owner Write

Anyone can view, but only the owner can modify:

Public profiles pattern

-- Anyone can read profiles CREATE POLICY "Public profiles are viewable" ON profiles FOR SELECT USING (true);

-- Only owner can update CREATE POLICY "Users can update own profile" ON profiles FOR UPDATE USING (auth.uid() = id);

Team-Based Access

Users can access data from their team:

Team access pattern

CREATE POLICY "Team members can view team data" ON projects FOR SELECT USING ( team_id IN ( SELECT team_id FROM team_members WHERE user_id = auth.uid() ) );

Testing Your RLS Policies

  1. Log in as User A, create some data
  2. Log in as User B, try to view User A's data
  3. User B should see empty results or only their own data
  4. Try updating/deleting User A's data as User B (should fail)

What happens if I don't enable RLS in Supabase?

Without RLS enabled, anyone with your Supabase anon key can read, modify, or delete ALL data in your tables. Since the anon key is visible in your frontend code, this means any user could access every other user's data. This is one of the most common security mistakes in Supabase apps.

Do I need RLS if I only access the database from my backend?

If you only use the service_role key from your backend and never expose the anon key in frontend code, you could theoretically skip RLS. However, RLS is still recommended as defense in depth. It protects against bugs in your backend code that might accidentally expose data.

How do I test if my RLS policies are working?

In Supabase, use the SQL Editor to test policies by setting the role. Run SET ROLE authenticated; then try SELECT queries. You can also use the Supabase client logged in as different test users to verify they only see their own data.

Check Your RLS

Scan your Supabase project for missing RLS policies.

Start Free Scan
Security Glossary

What is Row Level Security (RLS)? Supabase Guide