Symptoms / Fix guide

How do I know if Supabase RLS is off, and what do I do?

Matt Turley, Continuum. Updated November 5, 2026.

For founders with a Supabase backend, usually generated by an AI builder, who want to know whether strangers can read or change their users' data.

The short answer

How do I know if Supabase RLS is off, and what do I do?

Query pg_tables for rows where rowsecurity is false, or open the Security Advisor in the Supabase dashboard. A table in the public schema with RLS off can be read and written by anyone who holds your public anon key, if the role has been granted access. Turn RLS on and write the policies in the same migration.

What is really going on

Supabase exposes your Postgres tables through an API. The key your frontend uses (the anon key) is public by design: it is in your JavaScript bundle and anyone can copy it. What stops a stranger from using it to read the whole profiles table is not the key, it is row level security, the policies that decide which rows each caller may touch.

When RLS is off for a table that the API roles can reach, there is no per-row check at all. When it is on with no policies, nothing is readable, which is safe but breaks the app, and that is exactly why AI builders and tired founders switch it off or add a policy that allows everything.

Two things are easy to confuse. A missing GRANT returns a permission denied error (code 42501). A missing or permissive policy returns data you did not mean to return. This page is about the second. Our page on the Data API grant change covers the first.

Six ways RLS fails, most common first

  1. RLS is simply not enabled on the table

    How to tell The query on pg_tables shows rowsecurity = false for a table in public, or the Table Editor shows the RLS disabled warning. Tables created by SQL migrations or by an AI builder's schema step are the usual ones, because enabling RLS is a separate statement.

    The fix Run alter table public.your_table enable row level security; and write the policies in the same migration so the app keeps working.

  2. RLS is on, but the policy allows everyone

    How to tell A policy with using (true) or with check (true) for the anon or authenticated role. The padlock looks closed and the table is wide open. A policy named something like "Enable read access for all users" is the classic.

    The fix Replace it with a condition tied to the caller, for example user_id = (select auth.uid()), and decide deliberately which tables, if any, should be public.

  3. The policy trusts data the user can edit

    How to tell Policies that read role or tenant information from user_metadata in the JWT. Users can change their own user_metadata, so a policy built on it can be satisfied by the attacker.

    The fix Base authorization on app_metadata (which users cannot edit) or on a membership table the user cannot write to.

  4. A view or function bypasses the policies

    How to tell Views run with their owner's rights by default, so a view over a protected table can return everything. A security definer function exposed through the API runs with its creator's rights.

    The fix Create views with security_invoker = true, keep security definer functions out of exposed schemas, and give each one an explicit permission check inside.

  5. Storage buckets are public or have no policies

    How to tell Uploaded files (avatars, invoices, documents) open without being signed in, or the storage.objects table has permissive policies. RLS on your tables does nothing for files.

    The fix Make private buckets private, serve files through short-lived signed URLs, and scope the storage policies to the owner's folder.

  6. The service role key is in client code

    How to tell A search of your frontend bundle or environment shows SUPABASE_SERVICE_ROLE_KEY, or a variable with that key behind a NEXT_PUBLIC_ prefix. The service role bypasses RLS entirely.

    The fix Remove it from the client immediately, rotate it in the dashboard because it should be treated as exposed, and use it only in server code.

How we diagnose it

  1. List every table and its RLS state. Query pg_tables for the public schema and read rowsecurity. Then list the policies from pg_policies. A table with RLS on and zero policies, and one with RLS off, are both findings.
  2. Read each policy for what it actually allows. We read the using and with check expressions for the roles anon and authenticated and flag any that are always true, ignore the caller or depend on a user-editable claim.
  3. Test as a stranger. Request each table's REST endpoint with only the public anon key, as an unauthenticated caller. Any rows returned, or any successful write, is a confirmed exposure.
  4. Test as the wrong user. Sign in as user A and try to read, change and delete user B's rows. Per-user isolation is where policies written quickly fail even when RLS is on.
  5. Check views, functions, storage and keys. Look for views without security_invoker, exposed security definer functions, public buckets and the service role key anywhere in client code or build output.

What fixing it changes

  • RLS is on for every table in an exposed schema, with a policy written for each real access pattern.
  • No policy is unconditionally true unless the data is deliberately public, and that is a written decision.
  • Authorization depends on values users cannot edit.
  • Views, functions and storage buckets follow the same rules as tables.
  • Any key that was exposed is rotated, and a test checks stranger and wrong-user access on every deploy.

When you do not need us

If the advisor flags a few tables and your policies are simple (a user owns their own rows), the fix is a handful of SQL statements you can write with an AI assistant and verify with the stranger test above. Pay for a review when the data is sensitive, when policies depend on teams or roles, or when you cannot say with confidence what the anon key can reach today.

Your next step

The RLS checker is free and runs in your browser: paste your schema and it flags tables with RLS off and permissive policies. If it finds something on data that matters, the Supabase security review gives ranked findings with corrected SQL and a proof for each.

your schema into the free RLS checker: Paste your Supabase schema or policies and it flags tables with row level security off, permissive policies and risky grants. It runs in your browser, so the SQL is not sent anywhere. Or: Get the Supabase security review.

Paste your schema into the free RLS checker →See all services and prices →
FAQ

Straight answers

Is the Supabase anon key a secret?
No. It is designed to be public and ships in your frontend. That is only safe because row level security limits what it can do. If RLS is off or permissive, the public key becomes a key to your data.
I turned RLS on and now my app shows no data. Why?
With RLS on and no policy, access is denied by default. That is the safe state. Add a policy for each legitimate access, for example letting a signed-in user select rows where their id matches the row's owner.
Does RLS protect me if my service role key leaks?
No. The service role bypasses RLS. If it has been exposed, rotate it right away and assume the data was readable while it was public.
How is this different from a missing GRANT?
A missing GRANT makes the API answer permission denied, so the app breaks. A missing or permissive RLS policy makes the API answer with data, so the app works and the exposure goes unnoticed. The second is the dangerous one.
Keep reading
What breaks in an AI-built B2B SaaS app before launch?By vertical. For B2B SaaS built with AI tools: tenant isolation, roles, invites, billing and the enterprise asks that arrive first. What breaks and how to check.What breaks in an AI-built health app before launch?By vertical. Health apps built with AI tools: whether HIPAA applies, vendors that need a BAA, patient data in logs and AI prompts, and access control. Not legal advice.What breaks in an AI-built education app?By vertical. Education apps built with AI tools: student data, teacher, student and parent roles, children's privacy and what schools ask. Not legal advice.What should I check before launching my AI-built app?By stage. A launch-week checklist for apps built with AI tools: who can see whose data, secrets, the real payment path, email, backups, monitoring and a rollback plan.A customer sent us a security questionnaire. Where do we start?By stage. Your first customer security questionnaire: what they are really asking, how to answer honestly with evidence, and the controls to put in place first.Why is my OAuth callback not working?By symptom. Sign in with Google or GitHub fails after the provider screen. The redirect URI, Site URL, client secret and cookie causes, and how to check each.
Sources
  1. 1. Supabase: Row Level Security, Supabase: How RLS works, enabling it, policies, views and the warning about user_metadata.
  2. 2. Supabase: Securing your API, Supabase: The public anon key, the service role key and what RLS protects.
  3. 3. PostgreSQL: CREATE POLICY, PostgreSQL: The semantics of USING and WITH CHECK.
How do I know if Supabase RLS is off, and what do I do?