---
title: "How to enable RLS in Supabase: co vrátí po každém kroku"
description: "How to enable RLS in Supabase z dashboardu nebo v SQL, vyzkoušeno na lokálním stacku: co dostane veřejný klíč, dva uživatelé a tajný klíč po každém kroku."
url: "https://madsalt.tech/cs/blog/how-to-enable-rls-in-supabase/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/cs/about/"
language: "cs"
published: 2026-11-04
---

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

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](https://madsalt.tech/cs/tools/supabase-security-check/) 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](https://madsalt.tech/cs/blog/supabase-anon-key-vs-service-role-key/).

## 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](https://github.com/supabase/supabase/commit/33bd798d142183e162c25debcc50c8c711e68f66) 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](https://supabase.com/docs/guides/api/securing-your-api) od Supabase říká: otevřete Database, pak Policies a u tabulky zvolte „Enable RLS“.
- **V SQL**, v SQL editoru nebo v migraci:

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

```sql
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](https://madsalt.tech/cs/blog/supabase-rls-policy-examples/).

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.](https://madsalt.tech/charts/cs/how-to-enable-rls-in-supabase.svg)

*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](https://madsalt.tech/cs/blog/supabase-october-30-grant-change/).

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í:

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

```sql
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](https://madsalt.tech/cs/notes/supabase-security-definer-view/).

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

Event triggerem. [Průvodce event triggery](https://supabase.com/docs/guides/database/postgres/event-triggers) 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:

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

- Advisor v dashboardu hlásí totéž jako `rls_disabled_in_public`; co ta chyba znamená, je v [poznámce o RLS disabled in public](https://madsalt.tech/cs/notes/supabase-rls-disabled-in-public/).
- Bezplatná [Kontrola zabezpečení Supabase](https://madsalt.tech/cs/tools/supabase-security-check/) čte váš projekt jen pro čtení a vypíše tabulky s vypnutým RLS, ke kterým API dosáhne, views obcházející RLS a politiky, které povolují všem, ke každému s opravným SQL.
- Skener nemůže vědět, které řádky komu patří. Abyste to dokázali, pošlete požadavky jako dva uživatelé, jak je uvedeno výše. Celá metoda pro aplikaci s rolemi je v článku [Jak testovat Supabase RLS: 30 z 87 oprávnění nedělalo nic](https://madsalt.tech/cs/blog/how-to-test-supabase-rls/).

## 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](https://madsalt.tech/cs/#contact)
