---
title: "Supabase 30. října: co se rozbije a jak to ověřit"
description: "Od 30. října 2026 Supabase nově vytvořené tabulky v public nezpřístupní API automaticky. Co se rozbije, co funguje dál a čtyři kontroly, ověřeno lokálně."
url: "https://madsalt.tech/cs/blog/supabase-october-30-grant-change/"
author: "Denys Zaichenko"
language: "cs"
published: 2026-10-26
---

# Supabase 30. října: co se rozbije a jak to ověřit

TL;DR

Od 30. října 2026 Supabase na všech stávajících projektech přestane novým tabulkám v `public` automaticky přidělovat grants pro `anon`, `authenticated` a `service_role`. Tabulky, které už máte, fungují dál. Každá tabulka vytvořená později odpoví `supabase-js` i vašemu secret key chybou `42501 permission denied`, dokud ji migrace nepovolí. Přepnutí jsem si zreprodukoval lokálně: nové tabulky, nové views i staré tabulky, které migrace zahodí a vytvoří znovu, selhávají; existující tabulky, RPC funkce a vkládání do `serial` fungují dál. Zkontrolujete to dvěma SQL dotazy a jedním grepem níže a opravíte grantem, RLS a politikou ve stejné migraci, nikdy ne pomocí `grant all on all tables`.

Provozuji bezplatný [Supabase security checker](https://madsalt.tech/cs/tools/supabase-security-check/), který čte grants na každé vystavené tabulce, takže jsem chtěl přesně vidět, co tato změna udělá, než na ni lidé narazí. Vzal jsem lokální Supabase stack, nastavil mu stará výchozí nastavení, provedl přepnutí tak, jak ho popisuje Supabase, a poslal požadavek na každý druh objektu, který mě napadl. Tento článek ukazuje, co se vrátilo, a jak si před 30. ověřit vlastní projekt.

## Co se mění 30. října 2026?

Dosud dostala každá tabulka vytvořená ve schématu `public` přes výchozí oprávnění Postgresu práva `select`, `insert`, `update` a `delete` pro `anon`, `authenticated` a `service_role`. Proto byla nová tabulka dostupná z prohlížeče ve chvíli, kdy vznikla, ať už ji chránilo RLS, nebo ne. [Oznámení Supabase o breaking change](https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically) tato výchozí nastavení odstraňuje ve třech krocích:

| Datum           | Co se stane                                                                                 |
| --------------- | ------------------------------------------------------------------------------------------- |
| 28. dubna 2026  | Nové projekty se mohou při vytvoření odhlásit z „Automatically expose new tables“           |
| 30. května 2026 | Nové chování se stává výchozím pro nové projekty, nasazuje se postupně během několika týdnů |
| 30. října 2026  | Nové chování se použije na všechny stávající projekty                                       |

Na vašem projektu jsou to dva příkazy, tytéž, které Supabase uvádí pro dřívější přechod na vlastní žádost:

```sql
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;
```

Výchozí oprávnění se týkají jen objektů vytvořených později. Již existující grants se nemění, proto vaše současné tabulky fungují dál. Čeho se podle oznámení změna netýká: jiných schémat (`auth`, `storage`, `realtime`, vlastních), aplikací, které mluví s Postgresem přímým připojením (psql, ORM, server s connection stringem), a samotného RLS. Změna se týká Data API: REST, GraphQL a všech klientských knihoven nad nimi.

## Co se rozbije a co funguje dál?

Moje sestava: Supabase CLI 2.109.1, Postgres 17.6, PostgREST 14.14, dne 26. října 2026. V `config.toml` jsem nastavil `auto_expose_new_tables = true`, abych dostal stará výchozí nastavení, vytvořil jednu tabulku se dvěma řádky, zapnutým RLS a politikou pro čtení a pak spustil dva příkazy výše. Poté jsem vytvořil nové objekty a každý zavolal lokálním veřejným klíčem (`anon`) a secret klíčem (`service_role`), jedním požadavkem na každý.

![Co přestane odpovídat po přepnutí 30. října. Lokální Supabase, objekty vytvořené po revoke z changelogu, veřejný klíč, pokud není uvedeno jinak. Nová tabulka s RLS a politikou: create table → politika, bez grantu → 42501, HTTP 401; Stejná tabulka se secret key: create table → politika, bez grantu → 42501, HTTP 403; Nový view nad fungující tabulkou: create view → 42501, HTTP 401, bez hintu; Stará tabulka zahozená a znovu vytvořená migrací: drop + create table → grants pryč → 42501, HTTP 401.](https://madsalt.tech/charts/cs/supabase-october-30-grant-change.svg)

*Moje reprodukce na lokálním Supabase stacku (CLI 2.109.1, Postgres 17.6, PostgREST 14.14), 26. října 2026.*

![Co funguje i po přepnutí 30. října. Stejný lokální stack, stejný revoke, veřejný klíč. Tabulka vytvořená před přepnutím: existující tabulka → grants zůstaly → 2 řádky, HTTP 200; Nová funkce volaná přes RPC: create function → execute přes PUBLIC → HTTP 200; Nová serial tabulka, jen select a insert: id serial → zůstal UPDATE na sekvenci → insert, HTTP 201.](https://madsalt.tech/charts/cs/supabase-october-30-grant-change-works.svg)

*Moje reprodukce na lokálním Supabase stacku (CLI 2.109.1, Postgres 17.6, PostgREST 14.14), 26. října 2026.*

| Objekt, vytvořený po přepnutí, pokud není uvedeno jinak               | Klíč    | Výsledek                                                           |
| --------------------------------------------------------------------- | ------- | ------------------------------------------------------------------ |
| Tabulka vytvořená před přepnutím                                      | veřejný | HTTP 200, 2 řádky                                                  |
| Nová tabulka s RLS a politikou `to anon, authenticated`               | veřejný | HTTP 401, 42501, hint `GRANT SELECT ON public.new_orders TO anon;` |
| Stejná nová tabulka                                                   | secret  | HTTP 403, 42501, hint `… TO service_role;`                         |
| Nový view (`security_invoker`) nad starou tabulkou                    | veřejný | HTTP 401, 42501, `permission denied for view`, hint `null`         |
| Kopie vytvořená přes `create table … (like old_orders including all)` | veřejný | HTTP 401, 42501                                                    |
| Stará tabulka po `drop table` a novém `create table`                  | veřejný | HTTP 401, 42501                                                    |
| Nová funkce `public.ping()` přes `/rest/v1/rpc`                       | veřejný | HTTP 200                                                           |
| Nová tabulka s `id serial`, pro `anon` jen `select, insert`           | veřejný | insert HTTP 201                                                    |

V té tabulce je pět věcí, které stojí za to znát před přepnutím.

**„Existující tabulky si grants ponechají“ platí jen do chvíle, kdy tabulka existuje.** Migrace, která tabulku zahodí a vytvoří znovu, kopie přes `create table … (like …)` nebo obnova z dumpu vytvoří novou tabulku, a nová tabulka nedostane nic. Politika se mi vrátila s mým `create policy`, grants ne. Cokoli ve vašem pipeline, co tabulky přestavuje místo aby je upravovalo (alter), selže při prvním spuštění po přepnutí.

**Secret key se rozbije také.** `service_role` obchází RLS, ale grant stále potřebuje. Takže Edge Function, cron úloha nebo webhook handler, který zapisuje do nové tabulky, skončí s 403, nejen prohlížeč. Hint jmenuje `service_role`, podle toho to poznáte.

**Views žádný hint nedávají.** U tabulek PostgREST vrací přesný `GRANT`, který potřebujete. U mého nového view byl hint `null` a zpráva říkala `permission denied for view v_old_orders`. AI nástroj, který opravuje 42501 podle hintu, tu nemá co zkopírovat; grant je `grant select on public.v_old_orders to authenticated;` a view si má ponechat `security_invoker = true`, aby se na něj dál uplatňovalo RLS tabulky.

**Funkcí se tato změna netýká.** Ty dva příkazy se dotýkají tabulek a sekvencí. Moje nová funkce zůstala volatelná pro `anon`, `authenticated` i `service_role`, protože Postgres uděluje `execute` na každou novou funkci pro `PUBLIC` a revoke na úrovni schématu to odebrat nedokáže, jak vysvětluje [průvodce zabezpečením API od Supabase](https://supabase.com/docs/guides/api/securing-your-api). RPC volání po 30. říjnu fungují dál; zároveň to znamená, že nová funkce `security definer` je volatelná vaším veřejným klíčem hned, jak vznikne.

**Vkládání do `serial` fungovalo i bez grantu na sekvenci.** Mnoho rad tvrdí, že `serial` id po přepnutí potřebuje `grant usage on sequence … to authenticated`. V mém běhu insert prošel i bez něj: revoke odebírá `usage` a `select` na nových sekvencích, ale `update` zůstává a `update` na `nextval` stačí. Pokud odeberete všechna výchozí oprávnění na sekvencích, jako to dělá plný revoke v průvodci zabezpečením API, grant na `usage` potřebujete. Identity sloupec (`generated by default as identity`) nepotřebuje nic tak či tak.

Ještě jedna věc, kterou revoke nechává za sebou: nová tabulka stále přišla s `TRUNCATE`, `REFERENCES` a `TRIGGER` pro všechny tři role a na Postgresu 17 výchozí nastavení nese i `MAINTAIN`. Na `TRUNCATE` se žádná politika RLS neuplatní. Psal jsem o tom v [poznámce k 42501](https://madsalt.tech/cs/notes/supabase-42501-permission-denied/).

## Kde na to narazíte před 30. říjnem

Přepnutí na vašem hostovaném projektu proběhne 30., ale tři místa se už chovají jako po něm.

**`supabase db reset` na aktuálním CLI.** CLI v2.102.0 přidalo do `config.toml` klíč `[api] auto_expose_new_tables`. Na 2.109.1 měl můj čerstvý stack bez tohoto klíče už nová výchozí nastavení: tabulky dostaly jen `MAINTAIN`, `REFERENCES`, `TRIGGER` a `TRUNCATE`, sekvence jen `UPDATE`. Nastavení na `true` vrátí stará oprávnění a vypíše `api.auto_expose_new_tables is deprecated and will be removed on 2026-10-30`. Takže pokud vaše lokální tabulky po resetu stále fungují, nejdřív zkontrolujte tento klíč. Je to také nejlevnější místo k testování: odeberte ho, proveďte reset, spusťte aplikaci.

**Preview branches a projekty vytvořené od 30. května.** Přehrávají vaše migrace na nových výchozích nastaveních. Každá tabulka, jejíž migrace neobsahuje `grant`, je tam už nyní nedostupná. V [diskuzi na GitHubu](https://github.com/orgs/supabase/discussions/45329) lidé s jedním projektem na zákazníka hlásí přesně tohle: běžící projekty jsou v pořádku, každé nové nasazení ze stejných migrací je rozbité.

**Baseline migrace, která vrací staré výchozí nastavení.** Komentující ve stejné diskuzi upozorňuje, že první migrace vytvořená přes `supabase db pull` může obsahovat `alter default privileges … grant all on tables to anon, authenticated, service_role`. Ten příkaz jsem spustil po přepnutí: další tabulka, kterou jsem vytvořil, byla opět dostupná, HTTP 200. Lokálně to problém zakryje. Na vašem hostovaném projektu to změnu tiše vrátí zpět a každá budoucí tabulka je opět otevřená veřejnému klíči, ať má RLS, nebo ne. Projděte své migrace hledáním `alter default privileges` a přečtěte si každý nález.

## Jak zkontrolovat projekt před 30. říjnem

Čtyři kontroly od nejrychlejší po nejdůkladnější. První dvě běží v SQL Editoru hostovaného projektu.

### 1. Přepnul se už váš projekt?

Toto je dotaz z průvodce zabezpečením API od Supabase. Vypíše, co dostanou nové objekty vytvořené uživatelem `postgres` v `public`:

```sql
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;
```

Pokud řádky `tables` obsahují `SELECT, INSERT, UPDATE, DELETE`, je projekt stále na starém výchozím nastavení a přepne se 30. Pokud ukazují jen `MAINTAIN, REFERENCES, TRIGGER, TRUNCATE` (nebo žádné řádky), jsou nové tabulky už uzavřené. Vidíte to také v Dashboardu v nastavení Data API, „Automatically expose new tables“.

### 2. Které tabulky mají politiku, ale ne grant?

Politika pro roli, která nemá grant na daný příkaz, se nikdy nespustí: požadavek selže před RLS. Tento dotaz vypíše takové dvojice. V mé laboratoři našel novou tabulku a znovu vytvořenou starou, nic dalšího:

```sql
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;
```

Spouštějte ho po každé migraci, v CI nad čerstvým `db reset` nebo na stagingu. Na zdravém projektu nevrátí nic; po 30. je každý řádek tabulka, na kterou vaše aplikace nedosáhne. Kontroluje grants na úrovni tabulky, takže tabulka otevřená jen sloupcovými grants (`grant select (id, name) on …`) se tu objeví, i když jsou ty sloupce čitelné.

### 3. Které migrace vytvářejí tabulku bez grantu?

Databáze ukazuje jen tabulky, které existují. Migrace, kterou jste ještě nespustili, je vidět jen ve vašem repozitáři. Tento hrubý grep vypíše každý `create table` v `supabase/migrations` bez `grant … on` pro stejný název:

```bash
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
```

Je to porovnávání textu, ne parsování SQL: grant uvádějící několik tabulek v jednom příkazu, nebo tabulka jmenovaná na jednom místě bez schématu a na jiném se schématem, dá falešný nález. Přečtěte si každý vypsaný řádek. Tabulky, na které sahá jen serverový kód přímým připojením, grant nepotřebují vůbec.

### 4. Přehrát všechno na nových výchozích nastaveních

Odeberte `auto_expose_new_tables` z `config.toml`, spusťte `supabase db reset` a pusťte nad tím aplikaci nebo její testy. To je váš projekt po 30., navíc s čerstvou kopií každé tabulky, což je těžší případ. Supabase také vypisuje dotčené tabulky v Security Advisor a v Table Editoru označuje vystavení přes Data API.

## Jak to opravit, aniž byste otevřeli každou tabulku

Napište grant, RLS a politiky do migrace, která tabulku vytváří, a každé roli dejte jen to, co aplikace používá:

```sql
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);
```

Tento blok jsem spustil na stejném lokálním stacku: přihlášený testovací uživatel vložil řádek a přečetl ho zpět (HTTP 201, pak 200), veřejný klíč bez session dostal 42501 a kontrola 2 výše tabulku už nevypsala. Úvodní `revoke all` smaže `TRUNCATE`, `TRIGGER` a `REFERENCES`, které nová tabulka dostane, a na projektu, který se ještě nepřepnul, dává stejný výsledek. Roli `anon` dávejte grant jen na data určená odhlášeným návštěvníkům a `service_role` jen tehdy, pokud serverový kód používá na této tabulce secret key. Jak do sebe zapadají grants a politiky v celé aplikaci, popisuje můj [checklist zabezpečení aplikace z Lovable](https://madsalt.tech/cs/blog/lovable-app-security-checklist/).

Co nedělat, když přijde první 42501:

- **Nespouštějte `grant all on all tables in schema public to anon, authenticated`.** Vrátí to staré výchozí nastavení pro všechny tabulky najednou, včetně každé tabulky, kde RLS nebylo nikdy zapnuto, a ty se stanou čitelnými pro každého, kdo drží váš veřejný klíč. [Poznámka k 42501](https://madsalt.tech/cs/notes/supabase-42501-permission-denied/) ukazuje, proč je grant první zámek a RLS druhý.
- **Nepřidávejte znovu `alter default privileges … grant`.** Je to rollback celé změny.
- **Nevypínejte RLS „na zkoušku“ a nedávejte secret key do frontendu.** Secret key obchází RLS pro každého, kdo otevře váš bundle; rozdíl mezi klíči je v článku [anon key vs service role key](https://madsalt.tech/cs/blog/supabase-anon-key-vs-service-role-key/).

Pokud vám migrace píše AI nástroj, přidejte do jeho instrukcí jednu větu: „každá nová tabulka v public dostane explicitní grants pro každou roli, row level security a politiky zapni ve stejné migraci; nikdy negrantuj na all tables in schema public“. Hint v chybě ho naučí grant, nikdy ne RLS, které k němu patří.

## Otázky, které lidé kolem změny kladou

**Přestanou mé existující tabulky 30. října fungovat?** Ne. Výchozí oprávnění se týkají objektů vytvořených později a v mém běhu stará tabulka po přepnutí odpověděla HTTP 200. Výjimkou je tabulka, kterou vaše migrace zahodí a vytvoří znovu.

**Musím něco dělat, když nikdy nevytvářím tabulky?** Ne 30. Zkontrolujte, až příště tabulku přidáte vy, kolega nebo AI nástroj, a zkontrolujte každý pipeline, který vytváří projekty nebo větve z vašich migrací.

**Týká se to Edge Functions?** Ano, pokud používají `supabase-js` se secret key na nové tabulce: `service_role` potřebuje grant také. Funkce, které se připojují k Postgresu přímo, se to netýká.

**Týká se to RPC funkcí, Storage nebo Auth?** V této změně ne. Nové funkce zůstávají volatelné přes `PUBLIC` a `storage` s `auth` nejsou schéma `public`.

**Mohu přepnout dřív?** Ano. Dva příkazy na začátku jsou to přepnutí; spusťte je nejdřív na stagingu, pak na produkci. Existující tabulky si grants ponechají, takže běžící aplikace si ničeho nevšimne a další nová tabulka vám hned řekne, zda jsou vaše migrace připravené.

## Jak to vidí checker

Checker se připojuje jen pro čtení a čte grants na každé tabulce ve vašich vystavených schématech: do kterých tabulek může veřejný klíč zapisovat bez politiky, které tabulky mají RLS vypnuté a které výchozí oprávnění stále dávají každé nové tabulce `TRUNCATE`, `REFERENCES` nebo `TRIGGER`. Vypíše také každou politiku, jejíž role nemá pro daný příkaz grant, tedy tabulky, které najde kontrola 2, kromě těch s vypnutým RLS nebo se sloupcovými granty. Nevidí soubory migrací, které jste nespustili, takže kontrola 3 výše zůstává na vás. Pokud si nejste jistí právě RLS, provede vás jím [jak testovat Supabase RLS](https://madsalt.tech/cs/blog/how-to-test-supabase-rls/) a [poznámka o vystavených schématech](https://madsalt.tech/cs/notes/supabase-exposed-schemas/) pokrývá jiná schémata než `public`, kterých se tato změna netýká.
