Přejít na obsah

How to enable RLS in Supabase: co vrátí po každém kroku

Zobrazit jako MarkdownOtevřít v ClaudeOtevřít v ChatGPTOtevřít v Perplexity

Otázka se zkopíruje také. Pokud se chat otevře prázdný, vložte ji.

TL;DR

RLS v Supabase zapnete jedním příkazem na tabulku, alter table public.todos enable row level security;, nebo volbou „Enable RLS“ v dashboardu. Tabulky vytvořené v SQL editoru nebo migrací ho ve výchozím stavu nemají. Hned po zapnutí tabulka spíš ztichne, než aby křičela. V mém lokálním testu dostal veřejný klíč i dva přihlášení uživatelé u select, update a delete 200 [] a selhal jen insert, s 42501. Tajný klíč stále viděl všechny 3 řádky. Obyčejný pohled (view) nad tabulkou stále vydal všechny 3 řádky veřejnému klíči. Od 30. října 2026 navíc nová tabulka potřebuje grant, než se k ní cokoli dostane, takže zapněte RLS a napište politiky ve stejné migraci jako grant.

Provozuji bezplatnou Kontrolu zabezpečení Supabase a „RLS disabled“ je nález, který je vysvětlen nejhůř. Většina návodů říká, že po zapnutí RLS se „tabulka stane nepřístupnou“. U čtení to platí, u zápisů to klame. Proto jsem postavil jednu tabulku na lokálním Supabase stacku a po každém z kroků níže jí posílal všechny druhy požadavků: jako veřejný klíč, jako dva přihlášení uživatelé a jako tajný klíč.

Co je RLS v Supabase?

RLS (row-level security) je funkce Postgresu, která filtruje řádky podle požadavku. Supabase mění každý požadavek z supabase-js v dotaz do databáze, který běží jako role: anon pro veřejný klíč (anon, publishable), authenticated pro přihlášeného uživatele. Při zapnutém RLS Postgres kontroluje vaše politiky pro každý řádek a vrací, mění nebo maže jen ty řádky, které politika povolí. Při vypnutém RLS se každá role s grantem na tabulku dostane ke všem řádkům.

Tajný klíč (service role) je jiný. Jeho role má BYPASSRLS, takže se na něj RLS nikdy nevztahuje. Proto patří jen na server. Rozdíl mezi oběma klíči je v článku Supabase anon key vs service role key: co který otevře.

Jak zapnout RLS v Supabase?

Třemi způsoby, podle toho, jak tabulka vznikla:

  • Při vytváření tabulky v Table Editoru nechte zaškrtnuté „Enable Row Level Security (RLS)“. Ve zdrojovém kódu dashboardu je označeno „Recommended“ a při vypnutí se zobrazí varování, že tabulka bude veřejně čitelná a zapisovatelná.
  • U existující tabulky průvodce zabezpečením API od Supabase říká: otevřete Database, pak Policies a u tabulky zvolte „Enable RLS“.
  • V SQL, v SQL editoru nebo v migraci:
alter table public.todos enable row level security;

Průvodce Supabase výslovně uvádí, že tabulky vytvořené v dashboardu mají RLS zapnuté ve výchozím stavu a tabulky vytvořené v SQL editoru nebo jiným nástrojem ne. Pokud schéma tvoříte migracemi nebo AI nástrojem, zkontrolujte každou tabulku.

Co se stane hned po zapnutí RLS?

Nic nehlásí chybu kromě insertu. Vytvořil jsem public.todos se sloupcem vlastníka (user_id uuid default auth.uid()), udělil jí práva rolím API tak, jak to dělaly starší projekty, a přidal tři řádky: dva patřící uživateli A a jeden uživateli B. Potom jsem zapnul RLS bez jakékoli politiky:

Požadavek Veřejný klíč Uživatel A Uživatel B Tajný klíč
Select 200, 0 řádků 200, 0 řádků 200, 0 řádků 200, 3 řádky
Insert vlastního řádku 401, chyba 42501 403, chyba 42501 403, chyba 42501 201, řádek vložen
Update řádku 200, 0 řádků 200, 0 řádků 200, 0 řádků 200, 1 řádek
Delete řádku 200, 0 řádků 200, 0 řádků 200, 0 řádků 200, 1 řádek

Chyba insertu zní new row violates row-level security policy. Update a delete odpoví 200 s prázdným polem, takže aplikace, která nekontroluje vrácené řádky, hlásí „uloženo“, i když se nic neuložilo. Po zapnutí RLS si zápisy načtěte zpět, nebo si vyžádejte počet přes Prefer: count=exact.

Před RLS, se stejnými granty, mohl veřejný klíč přečíst všechny 3 řádky, vkládat a měnit nebo mazat libovolný řádek. Uživatel A mohl upravit a smazat řádek uživatele B a své řádky předat uživateli B.

Jak tabulku zase zprovoznit?

Napište politiku pro každou operaci. Obvyklá sada pro vlastní řádky:

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

S těmito politikami viděl uživatel A své 2 řádky a uživatel B svůj 1. Veřejný klíč stále viděl 0. Když se A pokusil vložit řádek patřící B nebo přesunout vlastní řádek k B, odpověď byla 403, 42501. Toto odmítnutí pochází z with check u update. Když se A pokusil změnit nebo smazat řádek uživatele B, odpověď byla 200, 0 rows. Další vzory (veřejné profily, týmy, MFA) jsou v článku Supabase RLS policy: příklady otestované přes API.

Jeden detail z tajného klíče: řádek, který vložil, dostal user_id = null, protože auth.uid() za tímto klíčem nemá žádného uživatele. RLS se přeskočilo, ale výchozí hodnota neudělala to, co naznačuje název sloupce.

Co četl veřejný klíč v jednotlivých krocích?

Řádky přečtené veřejným klíčem, ze 3. Jedna tabulka Supabase se 3 řádky, po každém kroku. Nová tabulka, bez grantu, RLS off (chyba 42501): 0; Grant přidán, RLS off: 3; RLS on, bez politiky: 0; RLS on, vlastní politiky: 0; Obyčejný view nad tabulkou: 3; View se security_invoker: 0.
Obrázek 1. Lokální Supabase stack (CLI 2.109.1, Postgres 17.6), 28. října 2026.

Dva kroky vyzradí všechno: grant bez RLS a obyčejný view nad tabulkou se správnými politikami. Další dvě sekce vysvětlují oba.

Potřebujete RLS i po změně grantů z 30. října?

Ano. Od 30. října 2026 Supabase už nedává novým tabulkám v public automatické granty pro role API, u nových i stávajících projektů. Můj lokální stack se už tak chová. Čerstvě vytvořená tabulka s vypnutým RLS odpověděla 42501 všem rolím, včetně tajného klíče, takže nic neuniklo. Podrobnosti jsou v článku Supabase 30. října: co se rozbije a jak to ověřit.

Grant se ale vrátí, jakmile aplikace tabulku potřebuje, a od té chvíle je RLS jediné, co stojí mezi veřejným klíčem a všemi řádky. Proto dejte všechno do jedné migrace, v tomto pořadí:

create table public.todos (...);
alter table public.todos enable row level security;
create policy "own select" on public.todos ...;
grant select, insert, update, delete on table public.todos to authenticated;

Udělte grant jen rolím, které tabulku potřebují. Když jsem roli anon odebral všechno, select veřejného klíče se změnil z 200 [] na 401, 42501. Grant je první zámek, RLS druhý.

Proč view stále propouští řádky?

Protože view běží s právy svého vlastníka, a ve Supabase je to postgres, který RLS obchází. Při platných politikách pro vlastní řádky na todos vrátil obyčejný create view public.todos_view as select * from public.todos všechny 3 řádky veřejnému klíči i uživateli B.

Po vytvoření znovu s with (security_invoker = true) vrátil stejný view veřejnému klíči 0 řádků, A 2 a B 1, přesně jako tabulka:

create view public.todos_view with (security_invoker = true) as
  select * from public.todos;

Advisor Supabase označuje obyčejný druh jako security_definer_view; co to znamená a jak to opravit, je v poznámce o Supabase view security definer.

Jak zapnout RLS na každé nové tabulce automaticky?

Event triggerem. Průvodce event triggery od Supabase uvádí funkci rls_auto_enable() a trigger ensure_rls, který běží po create table, create table as a select into v public. Nainstaloval jsem ho tak, jak je zveřejněn, a dostal:

  • Tabulka vytvořená potom příkazem create table měla RLS zapnuté bez jakéhokoli alter. Veřejný klíč dostal 200 [] u select a 401 u insert.
  • Tabulka vytvořená příkazem create table as select také měla RLS zapnuté.
  • Tabulka vytvořená před triggerem zůstala vypnutá. Trigger nepůsobí zpětně.

Bez triggeru měla druhá tabulka vytvořená vedle chráněné RLS vypnuté, a jakmile dostala grant, veřejný klíč ji volně četl i zapisoval. Pokrývá také jen public; ostatní vystavená schémata potřebují vlastní řádek ve funkci.

Pomáhá FORCE ROW LEVEL SECURITY?

Ne u rolí, o které byste se báli. alter table ... force row level security způsobí, že se RLS uplatní i na vlastníka tabulky. Ve Supabase mají postgres i service_role BYPASSRLS, takže vlastník po force stále napočítal 3 řádky, tajný klíč stále viděl všechno a view vlastněný postgres stále propouštěl. Změnilo se to jen u role vlastníka, kterou jsem vytvořil bez BYPASSRLS: 2 řádky před, 0 po.

Jak zkontrolovat vlastní projekt?

  • V SQL editoru vypište tabulky v public s vypnutým RLS:
select c.relname as table_name
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public'
  and c.relkind in ('r', 'p')
  and not c.relrowsecurity
order by 1;

Jak jsem testoval

  • Supabase CLI 2.109.1 na notebooku: Postgres 17.6, PostgREST 14.14, Auth 2.192.0. Dva uživatelé přihlášení heslem a lokální publishable a secret klíče.
  • Jedna tabulka, public.todos, před každým požadavkem znovu naplněná stejnými 3 řádky. Každý krok byl aplikován jako SQL a každý požadavek šel přes REST API, se zaznamenaným stavovým kódem a tělem.
  • Granty pro kroky 2 až 5 byly přidány ručně, aby odpovídaly projektu vytvořenému před 30. říjnem 2026.

Limity. Jeden lokální stack, vymyšlená data, 28. října 2026. Kroky v dashboardu jsou citovány z dokumentace Supabase, ne proklikány. Tabulky vytvořené vlastní rolí dashboardu mohou mít jiné výchozí granty. To jsem netestoval, stejně jako Storage, Realtime nebo GraphQL.

Nejste si jistí, že jsou vaše tabulky zamčené?

Rád se podívám na schéma spolu s vámi. Bez nabídky.

Ozvěte se

© 2026 Denys Zaichenko. Původní článek: https://madsalt.tech/cs/blog/how-to-enable-rls-in-supabase/