RLS Radar

Supabase Oct 30, 2026 Data API change: what it means for RLS

By RLS RadarUpdated Reviewed against the official Supabase changelog and docs
Short answer

On October 30, 2026, Supabase applies its new default to all existing projects: new tables you create in the public schema are no longer exposed to the Data API (REST and GraphQL) automatically. They need an explicit GRANT. Existing tables keep their current grants and keep working. RLS does not change: a grant decides whether a role can reach a table, RLS decides which rows.

Source: Supabase changelog: Tables not exposed to Data and GraphQL API automatically (checked 2026-10-17).

Timeline (from the Supabase changelog)

  • 2026-04-28: opt-in setting available when creating a new project.
  • 2026-05-30: becomes the default for all new projects (gradual rollout).
  • 2026-10-30: applied to all existing projects.

Supabase says Security Advisor flags affected tables until then, and that it emails owners and admins of active projects, with a final notice planned for 2026-10-23.

What changes, and what does not

  • Changes: a new table in public no longer gets select, insert, update, delete for anon, authenticated and service_role by default. Until you grant, the Data API answers with error 42501 (permission denied for table ...) and a hint naming the missing GRANT.
  • Does not change: existing tables keep their grants. enable row level security and create policy work exactly as before.
  • Not affected: apps that only use a direct Postgres connection (psql, an ORM, or an app server with a connection string), and tables in storage, auth, realtime or custom schemas.
  • Affected: anything that uses supabase-js, the client libraries, /rest/v1/ or /graphql/v1, plus migrations, CLI scripts and AI coding tools that create tables and expect them to be reachable immediately.

The real risk: copy-pasting the wrong fix

After Oct 30, the first symptom will be a 42501 error on a new table. The fastest-looking fix is a blanket grant to every role. That puts you back where you were before the change, or worse: Supabase warns that tables exposed through the Data API without RLS can be accessed by any role with matching grants (Securing your API).

So a grant to anon on a table without RLS means anyone holding your public (publishable) key, which ships in your frontend by design, can reach that table. And Supabase notes that adding policies does not take grants back (Row Level Security). Treat grant + RLS + policies as one unit, in one migration.

Safe pattern for every new table

-- 1. Enable RLS first (no policy = no rows for client roles)
alter table public.your_table enable row level security;

-- 2. Grant only what each role needs
--    Signed-out visitors: nothing, unless the data is truly public
grant select, insert, update, delete on table public.your_table to authenticated;
grant select, insert, update, delete on table public.your_table to service_role;

-- 3. Policies: each user sees and changes only their own rows
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);

Adapt your_table and user_id to your schema. For data that really is public (for example published blog posts), grant select to anon and add a for select to anon policy that only matches the public rows. service_role bypasses RLS, so keep its key server-side (see leaked service_role key).

Audit before and after Oct 30

List which tables the client roles can reach and whether RLS is on. Any row with a grant and rowsecurity = false needs attention now.

select g.table_name, g.grantee,
       string_agg(g.privilege_type, ', ' order by g.privilege_type) as privileges,
       t.rowsecurity
from information_schema.role_table_grants g
join pg_tables t on t.schemaname = g.table_schema and t.tablename = g.table_name
where g.table_schema = 'public' and g.grantee in ('anon', 'authenticated')
group by g.table_name, g.grantee, t.rowsecurity
order by t.rowsecurity, g.table_name, g.grantee;

This only reads catalog metadata, never row contents. More queries in How to check if Supabase RLS is enabled.

Opt in early (optional)

Supabase documents how to adopt the new behavior on an existing project today. These statements change defaults for future objects the postgres role creates in public; existing tables keep their grants:

alter default privileges for role postgres in schema public
  revoke select, insert, update, delete on tables from anon, authenticated, service_role;

alter default privileges for role postgres in schema public
  revoke usage, select on sequences from anon, authenticated, service_role;

To lock down an existing table you never meant to expose: revoke all on table public.your_table from anon, authenticated, service_role; (from the changelog). The Securing your API docs also list matching revokes for functions.

If an AI tool writes your migrations

Supabase's stated reason for the change is that agents, CLI scripts and AI platforms now create tables without a human reviewing each diff. Add this to your coding agent's instructions:

Every new table in public must ship in one migration with: enable row level security, the minimum grants per role (never a blanket grant to anon), and explicit policies for each operation using auth.uid(). Never fix a 42501 error by granting anon more than the policy needs.

FAQ

Do my existing Supabase tables stop working on October 30, 2026?

No. Supabase says existing tables keep their current grants and stay reachable. The change affects new tables created in the public schema after the change reaches your project.

Does a GRANT replace Row Level Security?

No. A grant decides whether a role can reach a table at all; RLS decides which rows it can see or change. Use both on every table exposed through the Data API.

Is it safe to run GRANT ALL to anon to fix the permission denied error?

Not on its own. Supabase warns that a table exposed through the Data API without RLS can be accessed by any role with matching grants. Grant only what each role needs, enable RLS, and add policies in the same migration.

Am I affected if I only use a direct Postgres connection?

No. Supabase says apps that only use psql, an ORM, or an app server with a connection string are not affected. It applies to the Data API (REST and GraphQL) used by supabase-js and the client libraries.

Sources

Check your live app in seconds

Free, read-only, permission-gated. Nothing stored.

Scan your app

Related guides