GeneralSupabaseIntermediate9 min read

How to Set Up Supabase Row Level Security (RLS)

Protect your Supabase database with Row Level Security policies. The most important security step for any vibe coded app using Supabase.

Before you start

  • A Supabase project with at least one table
  • Basic understanding of SQL

Step by step

1

Enable RLS on your tables

RLS is disabled by default. Enable it for every table that contains user data.

ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
ALTER TABLE comments ENABLE ROW LEVEL SECURITY;
2

Create a 'users see own data' policy

The most common policy: users can only read and modify their own rows.

CREATE POLICY "Users see own data"
ON profiles FOR ALL
USING (auth.uid() = id)
WITH CHECK (auth.uid() = id);
3

Create a 'public read' policy

For content that should be publicly visible (like blog posts), allow anyone to read.

CREATE POLICY "Public can read published posts"
ON posts FOR SELECT
USING (published = true);

CREATE POLICY "Authors can manage own posts"
ON posts FOR ALL
USING (auth.uid() = author_id)
WITH CHECK (auth.uid() = author_id);
4

Test your policies

Use the Supabase SQL editor to test policies by switching roles.

-- Test as an authenticated user:
SET request.jwt.claim.sub = 'user-uuid-here';
SET role TO 'authenticated';
SELECT * FROM profiles;

-- Test as anonymous:
SET role TO 'anon';
SELECT * FROM posts;
5

Handle service-role access

Your backend sometimes needs to bypass RLS (e.g., admin operations). Use the service role key for this.

// Server-side only — never expose in browser:
import { createClient } from '@supabase/supabase-js'

const supabaseAdmin = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_ROLE_KEY
)
// This client bypasses RLS

Common errors

Query returns empty array

RLS is blocking the query because no policy allows it.

Fix: Create a SELECT policy for the role you're using (authenticated or anon).

Insert fails with 'new row violates RLS'

Your INSERT policy's WITH CHECK clause rejects the row.

Fix: Make sure the WITH CHECK allows the user to insert. Common mistake: checking auth.uid() = id on a table where id is auto-generated.

Admin can't see all records

Even admin users are limited by RLS policies.

Fix: Use the service role key for admin operations, or create an admin-specific policy.

Related guides

Weekly Newsletter

Get next week's fix before you need it.

Join developers getting weekly vibe coding tips, error fixes, and tool updates.

Subscribe on Substack →