The complete guide to implementing RLS in Supabase. Protect your data with proper policies.
Test Your RLS ConfigurationSupabase creates tables with RLS disabled by default. In January 2025, 170+ apps built with Lovable were found to have exposed databases (CVE-2025-48757) because developers didn't enable RLS. 83% of exposed Supabase databases involve RLS misconfigurations.
-- Enable RLS on the table
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;-- Users can only see their own data
CREATE POLICY "Users can view own data"
ON your_table
FOR SELECT
TO authenticated
USING ((select auth.uid()) = user_id);
-- Users can only insert their own data
CREATE POLICY "Users can insert own data"
ON your_table
FOR INSERT
TO authenticated
WITH CHECK ((select auth.uid()) = user_id);
-- Users can only update their own data
CREATE POLICY "Users can update own data"
ON your_table
FOR UPDATE
TO authenticated
USING ((select auth.uid()) = user_id);
-- Users can only delete their own data
CREATE POLICY "Users can delete own data"
ON your_table
FOR DELETE
TO authenticated
USING ((select auth.uid()) = user_id);Important: Use (select auth.uid()) instead of auth.uid() for better performance. The select wrapper prevents re-evaluation on every row.
-- Test as anonymous user (should return 0 rows)
SELECT * FROM your_table;
-- Or use the Supabase Dashboard:
-- Authentication → Policies → Test your policiesMost common pattern - each user can only access their own rows:
CREATE POLICY "Users own their data"
ON posts
FOR ALL
TO authenticated
USING ((select auth.uid()) = author_id);Anyone can read, but only owners can modify:
-- Anyone can read
CREATE POLICY "Public read access"
ON posts
FOR SELECT
TO anon, authenticated
USING (true);
-- Only owners can write
CREATE POLICY "Owners can modify"
ON posts
FOR UPDATE
TO authenticated
USING ((select auth.uid()) = author_id);Users can access data belonging to their team:
CREATE POLICY "Team members can access"
ON projects
FOR ALL
TO authenticated
USING (
team_id IN (
SELECT team_id FROM team_members
WHERE user_id = (select auth.uid())
)
);Forgetting to enable RLS
Creating policies without enabling RLS does nothing. Always run:
ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;Using auth.uid() without select wrapper
This causes performance issues - the function re-evaluates for every row:
-- Bad: USING (auth.uid() = user_id)-- Good: USING ((select auth.uid()) = user_id)Creating service_role policies
Service role bypasses RLS automatically. Adding a policy for it creates warnings:
-- Bad: Creates multiple_permissive_policies warning-- Good: Just use TO authenticated (service_role bypasses RLS)Enabling RLS without any policies
RLS with no policies = no one can access data, not even authenticated users. Always create at least one policy after enabling RLS.
SELECT * FROM your_tableUse Vibe App Scanner to automatically test your deployed app:
Supabase Row Level Security (RLS) is a PostgreSQL feature that lets you control which rows users can access in your database. You create policies that define rules like "users can only read their own data" using SQL conditions. It's the primary way to secure your Supabase database.
No, RLS is disabled by default when you create tables in Supabase. You must manually enable RLS on each table using ALTER TABLE table_name ENABLE ROW LEVEL SECURITY and then create policies. This is the #1 cause of security issues in Supabase apps.
If you enable RLS without creating any policies, no one can access the data - not even authenticated users. RLS with no policies means "deny all access by default." You must create at least one policy to allow access.
Don't guess whether your RLS is configured correctly. Scan your deployed app to find misconfigurations before attackers do.
Scan Your App NowLast updated: January 15, 2026