RLS Policy Generator & Explainer
Row-Level Security is where most Supabase leaks come from. Generate correct policies from a pattern, or paste a policy to see in plain English exactly what it allows.
-- RLS for public.orders (pattern: Owner-only (per-user rows))
ALTER TABLE "public"."orders" ENABLE ROW LEVEL SECURITY;
CREATE POLICY "orders_select_own" ON "public"."orders"
FOR SELECT TO authenticated
USING ("user_id" = auth.uid());
CREATE POLICY "orders_insert_own" ON "public"."orders"
FOR INSERT TO authenticated
WITH CHECK ("user_id" = auth.uid());
CREATE POLICY "orders_update_own" ON "public"."orders"
FOR UPDATE TO authenticated
USING ("user_id" = auth.uid())
WITH CHECK ("user_id" = auth.uid());
CREATE POLICY "orders_delete_own" ON "public"."orders"
FOR DELETE TO authenticated
USING ("user_id" = auth.uid());
Generate Supabase RLS policies from a pattern
Row-Level Security is the single most important setting in a Supabase project, and it is also the one people get wrong most often. The syntax is not hard, but the details matter: the wrong role, a missing WITH CHECK, or a stray USING (true) quietly opens a table to the whole internet. This generator removes the guesswork. Pick the access pattern you actually want and it writes policies that follow it exactly.
The patterns cover the cases that come up again and again:
- Owner-only. Each user reads and writes only their own rows, matched by a
user_idcolumn againstauth.uid(). This is the right default for profiles, orders, messages, and anything tied to one account. - Public read, no writes. Anyone can select, nobody can change anything through the API. Good for published content and reference data.
- Authenticated read or read and write. Any signed-in user can reach the table. Useful for shared, non-sensitive data inside an app.
- Admin claim only. Access is gated on a claim in the JWT, so only tokens you mark as admin get through.
- Service-role only. RLS on, no policy. The table is invisible to the browser and reachable only from your server. The safest option for secrets and internal tables.
Explain a policy you already have
Inheriting a Supabase project often means inheriting policies nobody remembers writing. Paste a CREATE POLICY statement into the explain tab and the tool describes, in plain English, who it lets in, which commands it covers, and what its USING and WITH CHECK conditions require. If a policy is effectively public, it says so, so you can catch an accidental USING (true) before it becomes a problem.
A quick example
Say you have an orders table with a user_id column. You want every customer to see and manage their own orders and nobody else's. Pick the owner-only pattern, set the owner column to user_id, and you get four policies, one each for select, insert, update, and delete, all keyed on user_id = auth.uid(). Paste the result into the Supabase SQL editor and the table is locked down in one run.
Frequently asked questions
- What is Row-Level Security in Supabase?
- Row-Level Security, or RLS, is a Postgres feature that decides which rows a given user is allowed to see or change. Supabase leans on it heavily: the anon and authenticated API roles can only reach a table the way its RLS policies allow. Turn RLS on, write policies, and the same query returns different rows depending on who is asking.
- Do these policies work with Supabase auth?
- Yes. The generated policies use auth.uid() and auth.jwt(), which are the helper functions Supabase exposes inside Postgres. A policy that compares user_id to auth.uid() lets a signed-in user reach only their own rows, with no extra backend code.
- What is the difference between USING and WITH CHECK?
- USING controls which existing rows a policy applies to, so it governs reads, updates, and deletes. WITH CHECK controls which new or changed rows are allowed, so it governs inserts and updates. A safe owner policy usually sets both to the same condition, so a user can neither read nor write rows that belong to someone else.
- If I turn on RLS with no policy, what happens?
- The table is locked to the anon and authenticated roles. They get nothing back, because RLS denies by default and there is no policy to allow anything. Only the service-role key, which bypasses RLS and is meant for server use, can still read and write. That is exactly what you want for a table the browser should never touch directly.
- Does this tool send my schema anywhere?
- No. The generator and the explainer both run entirely in your browser. Nothing you type is uploaded, logged, or stored. You can use it offline once the page has loaded.
- How do I test that a policy actually works?
- The generator writes correct SQL, but you still want to see it in action against real roles. A Suparbase account includes an RLS simulator that runs any query as anon, as authenticated, or as a specific user, live against your project, so you can confirm a policy allows and blocks exactly what you expect before you ship it.
Test these against real roles.
Suparbase's RLS debugger simulates any policy as anon, authenticated, or a specific user, live against your project, so you know it's right before you ship.