How-To Guide
How to Set Up Supabase Row Level Security
The most important security setting for your Supabase project
TL;DR
TL;DR
Enable RLS on every table. Write policies for SELECT, INSERT, UPDATE, and DELETE. Use auth.uid() to verify the user owns the data. Test with different users to make sure policies work. Never deploy with "allow all" rules.
Why This Matters
Without RLS, anyone with your Supabase URL and anon key can read and modify ALL data in your database. This is the #1 security issue in Supabase apps.
Step-by-Step Guide
1
Enable RLS on your table
In the Supabase Dashboard:
- Go to Table Editor
- Select your table
- Click on the "RLS disabled" button
- Click "Enable RLS"
Or run this SQL:
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
2
Create a SELECT policy
This controls who can read data:
-- Users can only read their own data
CREATE POLICY "Users can view own data"
ON your_table
FOR SELECT
USING (auth.uid() = user_id);
For public data that anyone can read:
-- Anyone can read published posts
CREATE POLICY "Public can view published posts"
ON posts
FOR SELECT
USING (is_published = true);
3
Create an INSERT policy
This controls who can create data:
-- Users can only insert data for themselves
CREATE POLICY "Users can insert own data"
ON your_table
FOR INSERT
WITH CHECK (auth.uid() = user_id);
4
Create UPDATE and DELETE policies
-- Users can only update their own data
CREATE POLICY "Users can update own data"
ON your_table
FOR UPDATE
USING (auth.uid() = user_id);
-- Users can only delete their own data
CREATE POLICY "Users can delete own data"
ON your_table
FOR DELETE
USING (auth.uid() = user_id);
5
Test your policies
In the Supabase SQL Editor, test as different users:
-- Test as a specific user
SET request.jwt.claims.sub = 'user-id-here';
-- Try to read data
SELECT * FROM your_table;
-- Try to read another user's data (should return empty)
SELECT * FROM your_table WHERE user_id = 'other-user-id';
-- Reset
RESET request.jwt.claims.sub;
Common Policy Patterns
Profiles Table
-- Anyone can view profiles
CREATE POLICY "Public profiles are viewable"
ON profiles FOR SELECT USING (true);
-- Users can update own profile
CREATE POLICY "Users can update own profile"
ON profiles FOR UPDATE USING (auth.uid() = id);
-- Users can insert their own profile on signup
CREATE POLICY "Users can insert own profile"
ON profiles FOR INSERT WITH CHECK (auth.uid() = id);
Posts with Author
-- Anyone can read published posts
CREATE POLICY "Published posts are public"
ON posts FOR SELECT USING (status = 'published');
-- Authors can read their own drafts
CREATE POLICY "Authors can view own drafts"
ON posts FOR SELECT USING (auth.uid() = author_id);
-- Only authors can update their posts
CREATE POLICY "Authors can update own posts"
ON posts FOR UPDATE USING (auth.uid() = author_id);
-- Only authors can delete their posts
CREATE POLICY "Authors can delete own posts"
ON posts FOR DELETE USING (auth.uid() = author_id);
Private Messages
-- Users can read messages they sent or received
CREATE POLICY "Users can view own messages"
ON messages FOR SELECT
USING (auth.uid() = sender_id OR auth.uid() = recipient_id);
-- Users can only send messages as themselves
CREATE POLICY "Users can send messages"
ON messages FOR INSERT
WITH CHECK (auth.uid() = sender_id);
Common Mistakes
- Using "allow all" policies:
USING (true)for SELECT/UPDATE/DELETE exposes data - Forgetting INSERT policies: Users might be able to insert data for other users
- Not testing policies: Always test with different user IDs
- Mixing up USING vs WITH CHECK: USING is for read, WITH CHECK is for write
Never Deploy With These Rules
-- DANGEROUS: Allows anyone to do anything
CREATE POLICY "Allow all" ON table FOR ALL USING (true);
How to Verify RLS is Working
- Open browser DevTools
- Look at Network tab for Supabase API calls
- Note the response - you should only see your own data
- Try modifying the request to fetch other user's data - should fail