Supabase RLS Policy Generator
Pick a real access pattern — owner-only, team-based, admin override, or public read — and get strict, separated SELECT/INSERT/UPDATE/DELETE policies with the `(select auth.uid())` performance wrapper, required indexes, security warnings, and a script to verify isolation between two real users.
- Owner-only, team, admin-override & public-read patterns
- Performance-optimized auth.uid() wrapper
- Required index & verification script generation
What is Row Level Security (RLS)?
Row Level Security is a PostgreSQL feature — and the core of Supabase's security model — that restricts which rows a query can see or modify based on who's making the request. Instead of filtering data in your application code (which is trivial to bypass by calling the API directly), RLS enforces the rule inside the database itself, so it applies no matter how the table is queried — from your Next.js app, the Supabase dashboard, or a leaked anon key.
How to use this generator
- Enter your table name and the column that stores the row's owner (usually
user_id). - Pick an access pattern — owner-only, team-based, admin override, or public read.
- Fill in the extra fields that pattern needs (e.g. your team membership table for the team pattern).
- Toggle index and verification script generation on or off.
- Copy the generated SQL into the Supabase SQL editor, then run the verification script with two real test accounts.
Why not just write RLS policies by hand?
Hand-written RLS is where most Supabase data leaks actually come from — a missing with check clause, a forgotten index that makes policies slow at scale, or an unwrapped auth.uid() call that Postgres re-evaluates per row instead of once per statement. This generator bakes in the fixes Supabase's own performance and security docs recommend, so you don't have to remember them for every table you write.
Why this generator is strict by default
Most RLS snippets you find online use a single permissive policy like using (true) or a catch-all FOR ALL, which silently exposes more than intended and is harder to reason about per operation. This tool instead generates one policy per operation, always scopes them with TO authenticated (or an explicit anon grant when you actually want public reads), and wraps Supabase Auth helper functions in select so Postgres can cache them per statement instead of re-evaluating per row.
For team and admin patterns, it also generates a SECURITY DEFINER helper function rather than inlining a join inside the policy — this avoids the classic recursive-RLS trap where a policy on table A queries table B, which has its own RLS that queries table A again.
Who should use this tool?
This generator is useful for:
- Next.js developers using Supabase
- SaaS applications
- Multi-tenant applications
- Team collaboration apps
- Admin dashboards
- PostgreSQL projects using Row Level Security
Generated SQL includes
- ENABLE ROW LEVEL SECURITY
- Separate SELECT policies
- INSERT policies
- UPDATE policies
- DELETE policies
- WITH CHECK clauses
- Recommended indexes
- Verification scripts
- SECURITY DEFINER functions
Frequently asked questions
Why not just use "using (true)" for everything?
Because it silently exposes every row to every request that passes your anon key, which is the exact misconfiguration behind large-scale Supabase data leaks. This generator only uses an open read policy for the Public Read pattern, and warns you explicitly when it does.
Why does the SQL wrap auth.uid() in a select statement?
Wrapping helper functions like auth.uid() and auth.jwt() in a select causes Postgres to run an initPlan and cache the result once per statement instead of re-evaluating it for every row. Supabase's own performance testing shows this can be a 100x improvement on large tables.
Why are there 4 separate policies instead of one FOR ALL policy?
PostgreSQL policies don't support multiple operations in a single FOR clause, and separating SELECT, INSERT, UPDATE, and DELETE makes it obvious exactly what each operation allows. It also lets you apply WITH CHECK only where it's actually needed, for writes.
Do I still need to test this manually?
Yes. The Supabase SQL editor runs as the postgres role and bypasses RLS entirely, so a policy that looks correct there can still be broken for real users. Use the generated verification script with two real test accounts before shipping.
Does this tool send my schema anywhere?
No. Everything runs in your browser — table and column names you type are only used to generate the SQL shown on this page, nothing is sent to a server.