Milo Kits
Guide

Supabase "Table publicly accessible" (rls_disabled_in_public): what it means and how to fix it

"Table publicly accessible" (lint 0013_rls_disabled_in_public) means a table in your public schema has Row Level Security turned off. Supabase's docs say anyone with your project URL can then read, edit and delete every row through the API, and your project URL and public key are already in your app's JavaScript. The fix is alter table public.your_table enable row level security;, then adding policies for what your app should allow, because with RLS on and no policies the public key can't read the table at all.

What the warning means

Supabase's Security Advisor shows this as "Table publicly accessible", level ERROR. Supabase's own explanation: tables in the public schema are reachable through its APIs, and without Row Level Security "anyone with the project's URL can CREATE/READ/UPDATE/DELETE (CRUD) rows" (Supabase Advisors docs).

That sounds like it needs a secret. It doesn't. Your project URL and public (anon or publishable) key are in the JavaScript every visitor downloads. That's by design, and it's safe only when RLS is on with the right policies (see our anon key guide).

Find every affected table

Open Advisors → Security Advisor in the Supabase dashboard, or run this read-only query in the SQL editor:

select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;

Rows with rowsecurity = false are the tables the warning is about. The CLI equivalent is supabase db advisors.

The fix, step 1: turn RLS on

alter table public.your_table enable row level security;

Supabase notes that after this, the anon role can't read or write the table through the API until you add policies. If your app suddenly shows empty lists, that's why. Don't fix it by switching RLS off again.

Step 2: add the policies your app needs

Write the rule in plain English first ("each user can see and edit only their own notes"), then turn it into one policy per action:

create policy "own rows: select" on public.your_table for select to authenticated using ((select auth.uid()) = user_id); create policy "own rows: insert" on public.your_table for insert to authenticated with check ((select auth.uid()) = user_id); create policy "own rows: update" on public.your_table for update to authenticated using ((select auth.uid()) = user_id) with check ((select auth.uid()) = user_id); create policy "own rows: delete" on public.your_table for delete to authenticated using ((select auth.uid()) = user_id);

For data that really is public, such as published posts, add a select-only policy for reading and keep writes owner-only. Avoid using (true) on anything else: the Advisor flags that too, as a permissive policy.

Fix prompt
Supabase's Security Advisor says these tables are publicly accessible (RLS disabled): [TABLES]. For each table, tell me in plain English who should be able to read and change its rows. Then enable RLS and write separate select, insert, update and delete policies to match, using (select auth.uid()) for ownership. Show me the SQL before running it and don't use using (true) unless the data is meant to be public.

Step 3: check it worked

  1. Re-run the Security Advisor. The ERROR should be gone. If "Security policy allows unrestricted access" appears instead, a policy is too broad.
  2. Test logged out. In a private window, replay one of your app's /rest/v1/your_table requests. A private table should return [] or a permission error.
  3. Test as a second user. Our two-account guide walks through it. Supabase is also piloting an RLS Tester in the dashboard (a feature preview, select queries only, per its changelog) that runs a query as a logged-out or logged-in user.

Related warnings to fix at the same time

If the table shouldn't be in the API at all

Supabase's docs give another option: if you don't use the Data API for that schema, remove it from Exposed schemas in API settings. For a single internal table, moving it to a schema that isn't exposed does the same job.

FAQ

Is rls_disabled_in_public serious?

Yes. Supabase marks it ERROR, its highest level, and describes it as critically unsafe: anyone with your project's URL can create, read, update and delete rows in that table through the API.

My app broke after I enabled RLS. What happened?

With RLS on and no policies, the API returns no rows to the anon and authenticated roles. That's expected. Add a select policy (and insert, update and delete policies if your app writes to the table) for the users who should have access.

Can I just ignore it if the table holds nothing private?

For a table that really is public, like a list of published blog posts, enable RLS anyway and add a select-only policy using (true) for reading. Without RLS, anyone can also edit and delete those rows.

Does enabling RLS affect my server code that uses the service_role key?

No. The service_role or secret key bypasses RLS, which is why it must never be in your frontend. Server code using it keeps working, so it has to check who the user is itself.

Does the 30 October 2026 Supabase change fix this for me?

Not for existing tables. Supabase's changelog says that from 30 October 2026, new tables in existing projects aren't exposed to the Data API until you grant access. Tables you already have keep their grants, so check them now.

Sources

This page is general information, not a security audit. Only test apps you own or have permission to test. Platforms and prices change, so check the linked sources for current details. Spotted something out of date? Email hello@milokits.com.