Gå til indhold

Supabase 30. oktober: hvad knækker, og hvordan tjekker du

Vis som MarkdownÅbn i ClaudeÅbn i ChatGPTÅbn i Perplexity

Spørgsmålet kopieres også. Hvis chatten åbner tom, så indsæt det.

TL;DR

Den 30. oktober 2026 holder Supabase op med automatisk at give nye tabeller i public grants til anon, authenticated og service_role på alle eksisterende projekter. Tabeller, du allerede har, virker videre. Hver tabel oprettet bagefter svarer 42501 permission denied til supabase-js og til din secret key, indtil en migration giver adgang. Jeg gengav skiftet lokalt: nye tabeller, nye views og gamle tabeller, som en migration dropper og opretter igen, fejler alle; eksisterende tabeller, RPC-funktioner og serial-inserts virker videre. Tjek med to SQL-forespørgsler og en grep nedenfor, og ret med en grant, RLS og en policy i samme migration, aldrig med grant all on all tables.

Jeg driver en gratis Supabase security checker, der læser grants på hver eksponeret tabel, så jeg ville se præcis, hvad denne ændring gør, før folk begynder at ramme den. Jeg tog en lokal Supabase-stack, gav den de gamle standarder, udførte skiftet, som Supabase beskriver det, og sendte en forespørgsel til alle slags objekter, jeg kunne komme i tanke om. Dette indlæg viser, hvad der kom tilbage, og hvordan du tjekker dit eget projekt inden den 30.

Hvad ændrer sig den 30. oktober 2026?

Indtil nu fik hver tabel oprettet i schemaet public select, insert, update og delete til anon, authenticated og service_role gennem Postgres’ default privileges. Derfor kunne en ny tabel nås fra browseren i det øjeblik, den fandtes, beskyttet af RLS eller ej. Supabases breaking change-meddelelse fjerner de standarder i tre trin:

Dato Hvad sker der
28. april 2026 Nye projekter kan fravælge “Automatically expose new tables” ved oprettelsen
30. maj 2026 Den nye adfærd bliver standard for nye projekter, udrullet over nogle uger
30. oktober 2026 Den nye adfærd anvendes på alle eksisterende projekter

På dit projekt er skiftet to sætninger, de samme som Supabase giver til tidlig tilvalg:

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;

Default privileges gælder kun objekter, der oprettes senere. Grants, der allerede findes, røres ikke, og derfor virker dine nuværende tabeller videre. Hvad der ifølge meddelelsen ikke berøres: andre schemaer (auth, storage, realtime, dine egne), apps der taler med Postgres over en direkte forbindelse (psql, en ORM, en server med en connection string) og selve RLS. Ændringen handler om Data API’et: REST, GraphQL og alle klientbiblioteker oven på det.

Hvad knækker, og hvad virker videre?

Min opsætning: Supabase CLI 2.109.1, Postgres 17.6, PostgREST 14.14, den 26. oktober 2026. Jeg satte auto_expose_new_tables = true i config.toml for at få de gamle standarder, oprettede en tabel med to rækker, RLS slået til og en læsepolicy, og kørte så de to sætninger ovenfor. Bagefter oprettede jeg nye objekter og kaldte hvert af dem med den lokale offentlige nøgle (anon) og secret-nøglen (service_role), en forespørgsel hver.

Hvad holder op med at svare efter skiftet den 30. oktober. Lokal Supabase, objekter oprettet efter changelog-revoke, offentlig nøgle medmindre andet er angivet. Ny tabel med RLS og en policy: create table → policy, ingen grant → 42501, HTTP 401; Samme tabel med secret-nøglen: create table → policy, ingen grant → 42501, HTTP 403; Ny view over en tabel, der stadig virker: create view → 42501, HTTP 401, intet hint; Gammel tabel droppet og oprettet igen af en migration: drop + create table → grants væk → 42501, HTTP 401.
Min gengivelse på en lokal Supabase-stack (CLI 2.109.1, Postgres 17.6, PostgREST 14.14), 26. oktober 2026.
Hvad der virker videre efter skiftet den 30. oktober. Samme lokale stack, samme revoke, offentlig nøgle. Tabel oprettet før skiftet: eksisterende tabel → grants bevaret → 2 rækker, HTTP 200; Ny funktion kaldt over RPC: create function → execute via PUBLIC → HTTP 200; Ny serial-tabel, kun select og insert: id serial → UPDATE på sekvens bevaret → insert, HTTP 201.
Min gengivelse på en lokal Supabase-stack (CLI 2.109.1, Postgres 17.6, PostgREST 14.14), 26. oktober 2026.
Objekt, oprettet efter skiftet medmindre andet er angivet Nøgle Resultat
Tabel oprettet før skiftet offentlig HTTP 200, 2 rækker
Ny tabel med RLS og en to anon, authenticated-policy offentlig HTTP 401, 42501, hint GRANT SELECT ON public.new_orders TO anon;
Samme nye tabel secret HTTP 403, 42501, hint … TO service_role;
Ny view (security_invoker) over den gamle tabel offentlig HTTP 401, 42501, permission denied for view, hint null
Kopi lavet med create table … (like old_orders including all) offentlig HTTP 401, 42501
Den gamle tabel efter drop table og create table igen offentlig HTTP 401, 42501
Ny funktion public.ping() over /rest/v1/rpc offentlig HTTP 200
Ny tabel med id serial, kun select, insert givet til anon offentlig insert HTTP 201

Fem ting i den tabel er værd at vide før skiftet.

“Eksisterende tabeller beholder deres grants” gælder kun, så længe tabellen findes. En migration der dropper og genopretter en tabel, en create table … (like …)-kopi eller en gendannelse fra en dump laver en ny tabel, og en ny tabel får ingenting. Policyen kom tilbage med min create policy, grants gjorde ikke. Alt i din pipeline, der genopbygger tabeller i stedet for at ændre dem (alter), knækker ved første kørsel efter skiftet.

Secret-nøglen knækker også. service_role springer RLS over, men har stadig brug for en grant. Så en Edge Function, et cron-job eller en webhook-handler, der skriver til en ny tabel, fejler med 403, ikke kun browseren. Hintet nævner service_role, og sådan kender du forskel.

Views giver intet hint. For tabeller returnerer PostgREST den præcise GRANT, du skal bruge. For min nye view var hintet null, og beskeden lød permission denied for view v_old_orders. Et AI-værktøj, der retter 42501 ud fra hintet, har intet at kopiere her; grantet er grant select on public.v_old_orders to authenticated;, og viewet bør beholde security_invoker = true, så tabellens RLS stadig gælder.

Funktioner er ikke en del af ændringen. De to sætninger rører tabeller og sekvenser. Min nye funktion forblev kaldbar for anon, authenticated og service_role, fordi Postgres giver execute på hver ny funktion til PUBLIC, og et revoke på schemaniveau kan ikke fjerne det, som Supabases guide til API-sikkerhed forklarer. RPC-kald virker videre efter 30. oktober; det betyder også, at en ny security definer-funktion kan kaldes med din offentlige nøgle, så snart den findes.

serial-inserts virkede uden grant på sekvensen. Meget rådgivning siger, at et serial id efter skiftet kræver grant usage on sequence … to authenticated. I min kørsel gik inserten igennem uden: revoke fjerner usage og select på nye sekvenser, men update bliver, og update er nok til nextval. Hvis du tilbagekalder alle default privileges på sekvenser, som det fulde revoke i guiden til API-sikkerhed gør, skal du bruge usage-grantet. En identity-kolonne (generated by default as identity) kræver ingenting i begge tilfælde.

Én ting mere efterlader revoke: den nye tabel kom stadig med TRUNCATE, REFERENCES og TRIGGER til alle tre roller, og på Postgres 17 indeholder standarderne også MAINTAIN. Ingen RLS-policy gælder for TRUNCATE. Det gennemgik jeg i 42501-noten.

Hvor du møder det før 30. oktober

Skiftet på dit hostede projekt sker den 30., men tre steder opfører sig allerede som bagefter.

supabase db reset på en aktuel CLI. CLI v2.102.0 tilføjede [api] auto_expose_new_tables til config.toml. På 2.109.1, med nøglen udeladt, havde min friske stack allerede de nye standarder: tabeller fik kun MAINTAIN, REFERENCES, TRIGGER og TRUNCATE, sekvenser kun UPDATE. Sættes den til true, kommer de gamle grants tilbage, og der udskrives api.auto_expose_new_tables is deprecated and will be removed on 2026-10-30. Så hvis dine lokale tabeller stadig virker efter et reset, så tjek den nøgle først. Det er også det billigste sted at teste: fjern den, kør reset, kør din app.

Preview branches og projekter oprettet siden 30. maj. De afspiller dine migrationer på de nye standarder. Hver tabel, hvis migration ikke har noget grant, er allerede utilgængelig der. I GitHub-diskussionen rapporterer folk med ét projekt pr. kunde præcis dette: de kørende projekter er fine, hver ny udrulning fra de samme migrationer er knækket.

En baseline-migration, der bringer den gamle standard tilbage. En kommentator i samme diskussion peger på, at en første migration lavet med supabase db pull kan indeholde alter default privileges … grant all on tables to anon, authenticated, service_role. Jeg kørte den sætning efter skiftet: den næste tabel, jeg oprettede, kunne nås igen, HTTP 200. Lokalt skjuler det problemet. Anvendt på dit hostede projekt fortryder den stille ændringen, og hver fremtidig tabel er åben for den offentlige nøgle igen, uanset om den har RLS. Søg i dine migrationer efter alter default privileges og læs hvert fund.

Sådan tjekker du dit projekt før 30. oktober

Fire tjek, fra det hurtigste til det grundigste. De to første kører i SQL Editor på det hostede projekt.

1. Er dit projekt skiftet endnu?

Det er forespørgslen fra Supabases guide til API-sikkerhed. Den viser, hvad nye objekter oprettet af postgres i public vil få:

select
  case d.defaclobjtype
    when 'r' then 'tables'
    when 'S' then 'sequences'
    when 'f' then 'functions'
  end as object_type,
  a.grantee::regrole as role,
  string_agg(a.privilege_type, ', ' order by a.privilege_type) as privileges
from pg_default_acl d
cross join lateral aclexplode(d.defaclacl) a
where d.defaclrole = 'postgres'::regrole
  and d.defaclnamespace = 'public'::regnamespace
  and a.grantee in ('anon'::regrole, 'authenticated'::regrole, 'service_role'::regrole)
group by 1, 2
order by 1, 2;

Hvis tables-rækkerne indeholder SELECT, INSERT, UPDATE, DELETE, er projektet stadig på den gamle standard og skifter den 30. Hvis de kun viser MAINTAIN, REFERENCES, TRIGGER, TRUNCATE (eller ingen rækker), er nye tabeller allerede lukkede. Du kan også se det i Dashboard under Data API-indstillinger, “Automatically expose new tables”.

2. Hvilke tabeller har en policy, men ingen grant?

En policy for en rolle, der ikke har nogen grant til den kommando, kører aldrig: forespørgslen fejler før RLS. Denne forespørgsel viser de par. I mit laboratorium fandt den den nye tabel og den genoprettede gamle, intet andet:

select p.schemaname || '.' || p.tablename as "table", r.role, p.cmd
from pg_policies p
cross join lateral unnest(case when 'public' = any(p.roles) then array['anon', 'authenticated']::name[] else p.roles end) as r(role)
join pg_class c on c.relname = p.tablename
join pg_namespace n on n.oid = c.relnamespace and n.nspname = p.schemaname
where p.schemaname = 'public'
  and r.role in ('anon', 'authenticated')
  and not has_table_privilege(r.role::text, c.oid,
        case p.cmd when 'ALL' then 'SELECT' else p.cmd end)
order by 1, 2, 3;

Kør den efter hver migration, i CI mod et frisk db reset eller på staging. På et sundt projekt returnerer den ingenting; efter den 30. er hver række en tabel, din app ikke kan nå. Den tjekker grants på tabelniveau, så en tabel, der kun er åbnet med kolonne-grants (grant select (id, name) on …), dukker op her, selvom de kolonner kan læses.

3. Hvilke migrationer opretter en tabel uden grant?

Databasen viser kun tabeller, der findes. En migration, du ikke har kørt endnu, ses kun i dit repo. Denne grove grep viser hver create table i supabase/migrations uden grant … on for samme navn:

for t in $(grep -hioE 'create table( if not exists)? +[a-z0-9_."]+' supabase/migrations/*.sql | awk '{print $NF}' | sort -u); do
  grep -qiF "on $t " supabase/migrations/*.sql || grep -qiF "on table $t " supabase/migrations/*.sql || echo "no grant: $t"
done

Det er tekstmatching, ikke SQL-parsing: en grant, der nævner flere tabeller i én sætning, eller en tabel nævnt uden schema det ene sted og med det på et andet, giver et falsk hit. Læs hver linje, den udskriver. Tabeller, som kun serverkode når over en direkte forbindelse, behøver slet ingen grant.

4. Afspil alt på de nye standarder

Fjern auto_expose_new_tables fra config.toml, kør supabase db reset, og kør din app eller dens tests mod det. Det er dit projekt efter den 30., plus en frisk kopi af hver tabel, hvilket er det sværere tilfælde. Supabase viser også berørte tabeller i Security Advisor og markerer Data API-eksponering i Table Editor.

Sådan retter du det uden at åbne hver tabel

Skriv grant, RLS og policies i den migration, der opretter tabellen, og giv hver rolle kun det, appen bruger:

create table public.invoices (
  id bigint generated by default as identity primary key,
  user_id uuid not null default auth.uid(),
  total numeric not null
);

revoke all on table public.invoices from anon, authenticated, service_role;
grant select, insert on table public.invoices to authenticated;
grant select, insert, update, delete on table public.invoices to service_role;

alter table public.invoices enable row level security;

create policy "Owners read their invoices"
on public.invoices for select
to authenticated
using ((select auth.uid()) = user_id);

create policy "Owners add their invoices"
on public.invoices for insert
to authenticated
with check ((select auth.uid()) = user_id);

Jeg kørte denne blok på den samme lokale stack: en logget ind testbruger indsatte en række og læste den tilbage (HTTP 201, så 200), den offentlige nøgle uden session fik 42501, og tjek 2 ovenfor viste ikke længere tabellen. Det indledende revoke all fjerner de TRUNCATE, TRIGGER og REFERENCES, der følger med en ny tabel, og giver samme resultat på et projekt, der ikke er skiftet endnu. Giv kun anon adgang til data, der er beregnet til besøgende uden login, og service_role kun hvis serverkode bruger secret-nøglen på denne tabel. Hvordan grants og policies passer sammen i en hel app, står i min sikkerhedstjekliste til Lovable-apps.

Hvad du ikke skal gøre, når den første 42501 dukker op:

  • Kør ikke grant all on all tables in schema public to anon, authenticated. Det sætter den gamle standard tilbage for alle tabeller på én gang, også for tabeller hvor RLS aldrig blev slået til, og de bliver læsbare for alle med din offentlige nøgle. 42501-noten viser, hvorfor grantet er den første lås og RLS den anden.
  • Tilføj ikke alter default privileges … grant igen. Det er rollbacken af hele ændringen.
  • Slå ikke RLS fra for at “teste”, og læg ikke secret-nøglen i frontenden. Secret-nøglen springer RLS over for alle, der åbner din bundle; forskellen mellem nøglerne står i anon key vs service role key.

Hvis et AI-værktøj skriver dine migrationer, så tilføj en linje til dets instruktioner: “every new table in public gets explicit grants per role, enable row level security and policies in the same migration; never grant on all tables in schema public”. Hintet i fejlen lærer det grantet, aldrig den RLS, der hører sammen med det.

Spørgsmål, folk stiller om ændringen

Holder mine eksisterende tabeller op med at virke den 30. oktober? Nej. Default privileges gælder objekter oprettet senere, og i min kørsel svarede den gamle tabel HTTP 200 efter skiftet. Undtagelsen er en tabel, som dine migrationer dropper og opretter igen.

Skal jeg gøre noget, hvis jeg aldrig opretter tabeller? Ikke den 30. Tjek næste gang du, en kollega eller et AI-værktøj tilføjer en, og tjek enhver pipeline, der opretter projekter eller branches fra dine migrationer.

Påvirker det Edge Functions? Ja, hvis de bruger supabase-js med secret-nøglen på en ny tabel: service_role har også brug for en grant. Funktioner, der forbinder direkte til Postgres, påvirkes ikke.

Påvirker det RPC-funktioner, Storage eller Auth? Ikke i denne ændring. Nye funktioner kan stadig kaldes via PUBLIC, og storage og auth er ikke schemaet public.

Kan jeg skifte tidligt? Ja. De to sætninger øverst er skiftet; kør dem først på staging, så på produktion. Eksisterende tabeller beholder deres grants, så en kørende app mærker intet, og den næste nye tabel fortæller dig med det samme, om dine migrationer er klar.

Sådan ser checkeren det

Checkeren forbinder skrivebeskyttet og læser grants på hver tabel i dine eksponerede schemaer: hvilke tabeller den offentlige nøgle kan skrive til uden policy, hvilke tabeller har RLS slået fra, og hvilke default privileges der stadig giver hver ny tabel TRUNCATE, REFERENCES eller TRIGGER. Den viser også hver policy, hvis rolle ikke har nogen grant til den kommando, altså de tabeller, som tjek 2 finder, undtagen dem med RLS slået fra eller med kolonne-grants. Den kan ikke se migrationsfiler, du ikke har kørt, så tjek 3 ovenfor er stadig dit at køre. Hvis det er RLS, du er i tvivl om, gennemgår sådan tester du Supabase RLS det, og noten om eksponerede schemaer dækker andre schemaer end public, som denne ændring lader være i fred.

© 2026 Denys Zaichenko. Original artikel: https://madsalt.tech/da/blog/supabase-october-30-grant-change/