Back to blogs

How I Set Up Policies in Supabase (Step-by-Step Guide)

March 28, 2025
3 min read
How I Set Up Policies in Supabase (Step-by-Step Guide)

How I Set Up Policies in Supabase (Step-by-Step Guide)


When I first started using Supabase, I realized that setting up policies is essential for controlling who can access and modify data. Without policies, anyone could read or change my database, which is a big security risk.

Here’s a simple breakdown of how I set up Row Level Security (RLS) in Supabase.


Step 1: Enable Row Level Security (RLS)


Supabase uses Row Level Security (RLS) to manage data access. By default, RLS is disabled when you create a new table, meaning anyone can access the data.

To enable RLS:


  1. Open your Supabase dashboard
  2. Go to the table you want to secure
  3. Click on Security → Enable RLS


Once RLS is enabled, no one can access the table until you create policies.


Step 2: Setting Up Policies for Different Users


I needed to set up access for three types of users:

  1. Anonymous users (public access)
  2. Authenticated users (logged-in users)
  3. Service role (backend access only)


1. Anonymous Users (Read-Only Access)

I wanted anonymous users to be able to read data but not modify it. To do this, I created a policy:


CREATE POLICY "Allow public read access"
ON my_table
FOR SELECT
USING (true);


This means:

  1. Anyone can read the data
  2. No one can insert, update, or delete without authentication


In the frontend, I initialized Supabase with the anon key (public key):


const supabase = createClient(
'https://your-project.supabase.co',
'your-anon-key'
);


2. Authenticated Users (Access Their Own Data)

For logged-in users, I wanted them to:

  1. View and modify only their own data
  2. Not access other users' data


So, I created this policy:


CREATE POLICY "Authenticated users can access only their own data"
ON my_table
FOR ALL
USING (auth.uid() = user_id);


This means:

  1. auth.uid() gets the authenticated user's ID from Supabase
  2. The query only returns rows where user_id matches the logged-in user's ID


To ensure that the frontend always includes the user's token, I set up Supabase with global headers:


const supabase = createClient(
'https://your-project.supabase.co',
'your-anon-key',
{
global: {
headers: {
Authorization: `Bearer your-access-token`
}
}
}
);


With this setup, every request automatically includes the user's authentication token.


3. Service Role (Full Backend Access)

The service role key bypasses all policies and is meant for backend use only.

To allow full access in the backend, I used:


const supabase = createClient(
'https://your-project.supabase.co',
'your-service-role-key',
{
global: {
headers: {
Authorization: `Bearer your-service-role-key`
}
}
}
);


Important:

  1. The service role key should never be used in the frontend because it can override all security policies.


Final Thoughts


Once I set up these policies, my database was secure and well-structured. Here’s a quick summary:


User Type Permissions Policy
Anonymous UsersRead-onlyusing (true);
Authenticated UsersCan access their own datausing (auth.uid() = user_id);
Service RoleFull access (backend only)No policy restrictions


With this setup:


  1. Public users can only read data
  2. Logged-in users can only access their own data
  3. The backend has full control


Setting up policies correctly ensures data security and structured access control in Supabase.

Supabase Row Level SecuritySupabase access control policiesSecure Supabase database setupSupabase authentication rulesUser role-based access in Supabase