Critical Impact

Enable Row Level Security for Multi-Tenant Data

Database-enforced tenant isolation

Why This Matters

Without Row Level Security (RLS), all authenticated users can access all rows in your tables. This is a critical security vulnerability in multi-tenant applications where users should only see their own data.

RLS is Postgres's built-in mechanism for row-level access control. When enabled, every query is filtered by policies you define. This provides:

Warning: In Supabase, tables without RLS are accessible to all authenticated users by default. Always enable RLS on tables containing user data, even if you plan to add policies later.

The Problem

Without RLS enabled, any authenticated user can read, update, or delete any row:

Incorrect - No RLS protection
-- Table without RLS: all authenticated users can access all rows
CREATE TABLE documents (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL,
  title TEXT NOT NULL,
  content TEXT
);

-- Any authenticated user can query all documents
-- User A can see User B's documents!
SELECT * FROM documents;

This is a critical security vulnerability. User A can access User B's data simply by querying the table, even if your application code tries to filter by user_id.

The Solution

Enable RLS and create policies that restrict access based on auth.uid():

Correct - RLS enabled with user-based policy
-- Enable RLS on the table
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;

-- Policy: Users can only SELECT their own documents
CREATE POLICY "Users can view own documents"
  ON documents
  FOR SELECT
  USING (user_id = auth.uid());

-- Policy: Users can only INSERT documents for themselves
CREATE POLICY "Users can insert own documents"
  ON documents
  FOR INSERT
  WITH CHECK (user_id = auth.uid());

-- Policy: Users can only UPDATE their own documents
CREATE POLICY "Users can update own documents"
  ON documents
  FOR UPDATE
  USING (user_id = auth.uid())
  WITH CHECK (user_id = auth.uid());

-- Policy: Users can only DELETE their own documents
CREATE POLICY "Users can delete own documents"
  ON documents
  FOR DELETE
  USING (user_id = auth.uid());

The auth.uid() function returns the authenticated user's ID from Supabase Auth. Policies using USING filter existing rows, while WITH CHECK validates new/modified rows.

Important: After enabling RLS, you must create at least one policy for each operation (SELECT, INSERT, UPDATE, DELETE) you want to allow. Without policies, the table becomes inaccessible to all users except service role.

Force RLS on All Tables

To prevent accidentally creating tables without RLS, you can force RLS on all tables in a schema:

-- Force RLS on all existing tables in public schema
DO $$
DECLARE
  r RECORD;
BEGIN
  FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP
    EXECUTE 'ALTER TABLE ' || quote_ident(r.tablename) || ' ENABLE ROW LEVEL SECURITY';
  END LOOP;
END $$;

Result: With RLS enabled and proper policies, users can only access their own data. Even if application code has bugs or API endpoints are misconfigured, the database enforces tenant isolation.

Check Your Project Automatically

Connect your Supabase project to detect tables without RLS and other security issues.

Start Supabase Audit

Common Mistakes

How to Find Tables Without RLS

Use this query to find tables in your database that don't have RLS enabled:

SELECT 
  schemaname,
  tablename
FROM pg_tables
WHERE schemaname = 'public'
  AND tablename NOT IN (
    SELECT tablename 
    FROM pg_tables t
    JOIN pg_class c ON c.relname = t.tablename
    JOIN pg_namespace n ON n.oid = c.relnamespace
    WHERE c.relrowsecurity = true
      AND n.nspname = 'public'
  )
ORDER BY tablename;

Any tables returned by this query are vulnerable and should have RLS enabled immediately.

Learn More