Supabase Security

Supabase Row Level Security (RLS)

The complete guide to implementing RLS in Supabase. Protect your data with proper policies.

Test Your RLS Configuration

Critical: RLS is Disabled by Default

Supabase 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.

Quick Start: Enable RLS in 2 Minutes

1Enable RLS on Your Table

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

2Create a Policy for Authenticated Users

-- 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.

3Test Your Policies

-- Test as anonymous user (should return 0 rows)
SELECT * FROM your_table;

-- Or use the Supabase Dashboard:
-- Authentication → Policies → Test your policies

Common RLS Patterns

User-Owned Data

Most 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);

Public Read, Private Write

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);

Team-Based Access

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())
  )
);

Common RLS Mistakes

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.

How to Test Your RLS

Manual Testing

  1. Go to Supabase Dashboard → SQL Editor
  2. Run SELECT * FROM your_table
  3. As anon role, should return 0 rows (unless public)
  4. Test with different user JWTs
  5. Try INSERT/UPDATE/DELETE operations

Automated Testing

Use Vibe App Scanner to automatically test your deployed app:

  • Tests real-world RLS behavior
  • Attempts unauthorized data access
  • Checks all tables for misconfigurations
  • Provides remediation guidance
Test Your RLS

RLS Security Checklist

1
RLS is enabled on ALL tables containing user data
2
Every table with RLS has at least one policy
3
Policies use (select auth.uid()) not auth.uid()
4
No service_role key in frontend code
5
Policies specify TO authenticated or TO anon
6
Tested as anonymous user - cannot access data
7
Tested as authenticated user - can only access own data
8
INSERT policies use WITH CHECK, not USING

Frequently Asked Questions

What is Supabase Row Level Security?

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.

Is RLS enabled by default in Supabase?

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.

What happens if I enable RLS but don't create policies?

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.

Test Your Supabase RLS Configuration

Don't guess whether your RLS is configured correctly. Scan your deployed app to find misconfigurations before attackers do.

Scan Your App Now

Last updated: January 15, 2026