Supabase RLS Policy Generator
How do I write a Supabase RLS policy?
Turning Row Level Security on is one line; the hard part is the four policies underneath it, and getting select right while leaving insert wide open is the usual way a table stays exposed. Describe who should reach your table in plain English and copy out the SQL.
-- Row Level Security for public.profiles
-- Enabling RLS denies everything by default; each policy below adds back
-- exactly one allowed operation. Run this in the Supabase SQL editor.
alter table public.profiles enable row level security;
-- Re-running this file should be safe, so drop first.
drop policy if exists "profiles_select" on public.profiles;
drop policy if exists "profiles_insert" on public.profiles;
drop policy if exists "profiles_update" on public.profiles;
drop policy if exists "profiles_delete" on public.profiles;
create policy "profiles_select"
on public.profiles for select
to authenticated
using (auth.uid() = user_id);
create policy "profiles_insert"
on public.profiles for insert
to authenticated
with check (auth.uid() = user_id);
create policy "profiles_update"
on public.profiles for update
to authenticated
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
create policy "profiles_delete"
on public.profiles for delete
to authenticated
using (auth.uid() = user_id);
-- Verify: this should return zero rows for a signed-out caller.
-- select * from public.profiles;Generated in your browser - the table name never leaves this page. Read the policy before running it, and re-run the checker afterwards to confirm the table stopped answering.
What this generator produces
Enable Row Level Security
The alter table statement that switches RLS on. On its own it denies everything - each policy below then adds back exactly one allowed operation.
Read more →Owner-scoped access
Policies matching auth.uid() against your ownership column, so each signed-in user reaches only their own rows. The right default for profiles, orders, and messages.
Public-read tables
For published content: anyone may read, only the owner may write. Read access goes to anon and authenticated, writes stay owner-scoped.
Server-only tables
RLS on and no policies at all, so the anon and authenticated roles get zero rows. Reach it from server code with the service-role key.
Read more →All four operations covered
Separate select, insert, update, and delete policies, with with-check clauses on the writes so a user cannot insert or update a row into someone else's name.
Safe to re-run
Every policy is dropped before it is created, so pasting the SQL a second time updates your policies instead of erroring.
How the Supabase RLS Policy Generator works
- Step 1
Name your table and owner column
The table you want protected and the column holding the owner's user id - usually user_id. Nothing is sent anywhere; this runs in your browser.
- Step 2
Say who should reach it
Owner-only, public read with owner writes, any signed-in user, or nobody from the client at all. Each option explains the kind of table it suits.
- Step 3
Run it, then verify from outside
Paste the SQL into the Supabase SQL editor and run it. Then check the table from an anonymous request - our RLS checker does exactly that, using the anon key from your own bundle.
Frequently asked questions
How do I write a Supabase RLS policy?
A policy has three parts: the operation it governs (select, insert, update, or delete), the role it applies to (anon or authenticated), and the condition a row must satisfy. Reads use a using clause; writes use with check, which validates the row being written. For per-user ownership the condition is auth.uid() = user_id. You need one policy per operation you want to allow - enabling RLS with no policies denies everything, which is the safe starting point.
What is Row Level Security?
Row Level Security is a Postgres feature that decides, row by row, who may select, insert, update, or delete. Supabase exposes your tables over HTTP through PostgREST, so RLS is the only thing between a table and the public internet - the anon key that reaches it ships in your JavaScript by design. A table with RLS switched off is readable by anyone who opens DevTools and copies that key.
Why do I need separate insert and update policies?
Because a select policy only governs reading. This is the most common way a table stays exposed after someone believes they secured it: they write a policy limiting reads to the owner, see their own rows correctly, and never notice that insert and update were never restricted, so any signed-in user can still write rows attributed to somebody else. Write policies also need a with check clause, which validates the row being written rather than the rows being read.
Is it free? Do I need to sign in?
It is completely free and there is no account, no sign-in, and no limit on how many times you use it. Ubserve makes money from the paid security report, not from this page. The scanner it pairs with is free to run too - you get a grade, an issue count, and up to two findings in full before anything is asked of you.
Does my table name get sent to Ubserve?
Nothing leaves your browser. This generator is plain string assembly running on your own machine: the table name, column name, or header options you choose are never sent to Ubserve or anywhere else, and there is nothing for us to store. You can confirm it by opening DevTools and watching the Network tab while you use it - no request is made.
How do I confirm the policy actually worked?
Test it the way an attacker would, from outside and signed out. Request the table with only your public anon key and confirm nothing comes back - if rows still arrive, the policy is not doing what you think. Reading the policy in the dashboard is not the same test, because a policy can exist and still be permissive. Our free RLS checker runs this against your live app using the anon key from your own bundle.
What founders say
5.0 out of 5 - Ubserve, from 3 reviews
“Helped me find a critical database issue that would've done real damage. I recommend it for every founder.”
“Caught a critical CSP issue on my frontend within minutes. Don't ship without a scan.”
“The free scan took under 60 seconds to find real issues in my project.”
Other free tools
See all tools →Supabase RLS Checker
Probes your Supabase tables with the anon key from your own bundle to see which ones answer without Row Level Security.
Open tool →Firebase Rules Checker
Finds the Firebase config in your bundle and tests whether your Realtime Database answers an unauthenticated read.
Open tool →CSP Generator
Builds a Content Security Policy and the rest of your security headers as copy-paste config for Next.js, Vercel, Netlify, or Cloudflare Pages.
Open tool →Keep reading
- How to fix missing RLS in SupabaseThe longer walkthrough behind this generator.
- What is Row Level Security?The concept in plain language.
- Supabase RLS not working? Here's whyWhen a policy exists but protects nothing.
- Supabase security checklist for AI-built appsThe full pre-launch pass.
- Supabase service-role key exposureThe key that bypasses every policy you write.
Check what your live app serves
Anything you check here only covers what you hand it. What an attacker sees is what your deployed app serves, so paste your URL and check that too.