Fifty-two days from now, Supabase changes what happens to every table you create in an existing project. Not a setting you toggle - a platform default enforcement that lands on all projects at once.
Here is the quiet part nobody warns you about: the migration you already wrote and shipped is the one that breaks, and the leak that matters was probably already there before today.
What actually happens on October 30
Supabase published this as changelog entry #45329 on April 28, 2026. The dates:
| Date | What changes |
|---|---|
| 2026-05-30 | New projects default to opt-in exposure (grant required) |
| 2026-10-30 | The same rule is enforced on every existing project |
Before the change, a table in public was automatically granted select, insert, update, delete to anon, authenticated, and service_role - reachable through the Data API the moment it existed. After the flip, new tables in public are reachable only if your migration explicitly GRANTs the role.
Existing tables keep their current grants. That is the trap, in two opposite ways:
Trap 1 - your next migration breaks with a 403 that is silent in CI.
create table invoices (
id uuid primary key default gen_random_uuid(),
customer_id uuid not null,
amount_cents integer not null,
created_at timestamptz default now()
);
Deploy that after October 30 and the first SDK call dies in production with error 42501 permission denied for table invoices - PostgREST short-circuits at the privilege layer before RLS is ever evaluated.
Trap 2 - you are about to "fix" it by opening the whole database.
The first search hit for permission denied for table supabase is the barrel-load answer:
grant all on all tables in schema public to anon, authenticated;
It works. Your table appears in the API immediately. It also hands anonymous visitors select, insert, update, and delete on every table in the database - including and especially the ones you never meant to expose. On a project where RLS is off on even one table, this command is a full database exposure, and it is the exact CVE-2025-48757 class of leak (170 production apps, 303 endpoints, public anon key).
The leak that was already there
While you are thinking about October 30, this is the query that finds your current problem:
select n.nspname as schema,
c.relname as table,
c.relrowsecurity as rls_enabled,
coalesce(string_agg(
g.grantee || ':' || g.privilege_type, ', '
order by g.grantee), '(none)') as grants
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
left join information_schema.role_table_grants g
on g.table_schema = n.nspname
and g.table_name = c.relname
and g.grantee in ('anon', 'authenticated')
where n.nspname = 'public'
and c.relkind in ('r','p')
group by 1,2,3
order by c.relrowsecurity, c.relname;
-
rls_enabled = falsewith anyanon:...grant = every profile in your database returns to a stranger withcurland your anon key. That key ships in your JavaScript bundle. -
rls_enabled = falsewithanon:UPDATEon top = the same stranger can also rewrite rows. - The row that hurts looks like
profiles | false | anon:SELECT, anon:UPDATE.
And the anon key check, one curl:
curl -s "https://YOUR_PROJECT.supabase.co/rest/v1/profiles?select=*" \
-H "apikey: YOUR_ANON_KEY" | head -20
-
[]= RLS is doing its job. - rows = anonymous visitors can read this table.
-
42501= the grant did not apply.
What to ship so both traps are impossible
Make grants part of the migration file, next to the table, next to the policies.
create table invoices (
id uuid primary key default gen_random_uuid(),
customer_id uuid not null,
amount_cents integer not null,
created_at timestamptz default now()
);
grant select on public.invoices to anon; -- only if genuinely public
grant select, insert, update, delete on public.invoices to authenticated;
grant select, insert, update, delete on public.invoices to service_role;
alter table invoices enable row level security;
create policy "users see own invoices" on invoices
for select to authenticated using (customer_id = auth.uid());
Three rules:
- Grant to
authenticated, notanon. - Never
grant ... to anonwith RLS off. - Never
grant all on all tables ... to anonas the fix.
The honest version of the deadline
October 30 does not turn your RLS off. It does not touch existing tables. What it does is stop silently exposing the tables you create from now on - while leaving all the tables you already created exactly as open or closed as they were. The two jobs are separate: grant hygiene going forward, and an audit of what is already reachable today.
Runnable isolation check: https://github.com/cekuu35/supabase-rls-leak-demo
Free PDF checklist: https://cengokurtoglu.gumroad.com/l/nextjs-supabase-10-checks-free
What to do next (Complete 10-Product CTA Ladder)
| Need | Product | Price | Time |
|---|---|---|---|
| Quick self-check | 7-Surface Supabase Checklist (PDF) | Free | 2 min |
| Self-serve audit | RLS Audit Kit | $29 | 30 min |
| Cut bill 50% | Supabase Cost Optimization Report | $99 | 24h |
| Multi-tenant bug | Multi-tenant RLS Policy Fix | $199 | 24h |
| File upload holes | File Upload Security | $199 | 24h |
| Stripe webhook broken? | Lovable Stripe Webhook Rescue | $399 | 24-48h |
| Auth breaks on domain | Auth Production Deploy Package | $499 | 24h |
| Edge functions wide open | Edge Function Security Hardening | $299 | 24h |
| Deploy with confidence | Supabase Migration Review | $299 | 24h |
| All-in-one launch | Launch-Ready SaaS Backend Bundle | $599 | 48h |
| Admin panel missing | Admin Panel Scaffold | $399 | 24h |
| Ongoing monitoring | Guard $50/mo | $50/mo | Recurring |
Run the audit query above before October 30. The date is the excuse; the leak was the actual news.
Top comments (0)