TL;DR
Aktiver RLS i Supabase med én setning per tabell, alter table public.todos enable row level security;, eller med valget “Enable RLS” i dashbordet. Tabeller laget i SQL-editoren eller av en migrering får det ikke som standard. Rett etter at du har aktivert det, blir tabellen stille snarere enn høylytt. I min lokale test fikk den offentlige nøkkelen og to innloggede brukere 200 [] på select, update og delete, og bare insert feilet med 42501. Den hemmelige nøkkelen så fortsatt alle 3 rader. En vanlig view over tabellen ga fortsatt alle 3 rader til den offentlige nøkkelen. Siden 30. oktober 2026 trenger en ny tabell dessuten en grant før noe når den, så aktiver RLS og skriv policyer i samme migrering som granten.
Jeg driver en gratis Supabase security checker, og “RLS disabled” er funnet jeg ser forklart dårligst. De fleste guider sier at etter at du har aktivert RLS, “blir tabellen utilgjengelig”. Det stemmer for lesing og er misvisende for skriving. Så jeg bygde én tabell på en lokal Supabase-stack og sendte alle slags forespørsler til den, som den offentlige nøkkelen, som to innloggede brukere og som den hemmelige nøkkelen, etter hvert trinn nedenfor.
Hva er RLS i Supabase?
RLS (row-level security) er en Postgres-funksjon som filtrerer rader per forespørsel. Supabase gjør hver forespørsel fra supabase-js om til en databasespørring som kjører som en rolle: anon for den offentlige nøkkelen (anon, publishable), authenticated for en innlogget bruker. Med RLS på sjekker Postgres policyene dine for hver rad og returnerer, endrer eller sletter bare radene en policy tillater. Med RLS av når hver rolle med en grant på tabellen alle radene.
Den hemmelige nøkkelen (service role) er annerledes. Rollen har BYPASSRLS, så RLS gjelder aldri for den. Derfor hører den bare hjemme på en server. Forskjellen mellom de to nøklene står i Supabase anon key vs service role key: hva hver åpner.
Hvordan aktiverer du RLS i Supabase?
Tre måter, avhengig av hvordan tabellen ble laget:
- Når du lager en tabell i Table Editor, la “Enable Row Level Security (RLS)” stå avkrysset. I Supabases dashbordkode er det merket “Recommended”, og slår du det av, vises en advarsel om at tabellen blir offentlig lesbar og skrivbar.
- For en eksisterende tabell sier Supabases guide for å sikre API-et: gå til Database, deretter Policies, og velg “Enable RLS” på tabellen.
- I SQL, i SQL-editoren eller en migrering:
alter table public.todos enable row level security;
Supabases guide er tydelig på at tabeller laget i dashbordet har RLS på som standard, og at tabeller laget i SQL-editoren eller med et annet verktøy ikke har det. Bygger du skjemaet med migreringer eller et AI-verktøy, sjekk hver tabell.
Hva skjer rett etter at du har aktivert RLS?
Ingenting gir feil unntatt insert. Jeg laget public.todos med en eierkolonne (user_id uuid default auth.uid()), ga den grants til API-rollene slik eldre prosjekter har det og la til tre rader: to eid av bruker A og én av bruker B. Deretter aktiverte jeg RLS uten policy:
| Forespørsel | Offentlig nøkkel | Bruker A | Bruker B | Hemmelig nøkkel |
|---|---|---|---|---|
| Select | 200, 0 rader | 200, 0 rader | 200, 0 rader | 200, 3 rader |
| Insert egen rad | 401, feil 42501 | 403, feil 42501 | 403, feil 42501 | 201, rad satt inn |
| Update en rad | 200, 0 rader | 200, 0 rader | 200, 0 rader | 200, 1 rad |
| Delete en rad | 200, 0 rader | 200, 0 rader | 200, 0 rader | 200, 1 rad |
Insert-feilen lyder new row violates row-level security policy. Update og delete svarer 200 med en tom liste, så en app som ikke sjekker de returnerte radene, melder «lagret» når ingenting ble lagret. Les skrivingene dine tilbake etter at du har aktivert RLS, eller be om et antall med Prefer: count=exact.
Før RLS, med de samme grantene, kunne den offentlige nøkkelen lese alle 3 rader, sette inn, og oppdatere eller slette hvilken som helst rad. Bruker A kunne redigere og slette bruker Bs rad og gi bort sine egne rader til B.
Hvordan gjør du tabellen brukbar igjen?
Skriv en policy per operasjon. Det vanlige settet for egne rader:
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);
Med disse så bruker A sine 2 rader og bruker B sin 1. Den offentlige nøkkelen så fortsatt 0. Da A prøvde å sette inn en rad eid av B, eller å flytte sin egen rad til B, ble svaret 403, 42501. Den avvisningen kommer fra with check på update. Da A prøvde å oppdatere eller slette Bs rad, ble svaret 200, 0 rader. Flere mønstre (offentlige profiler, team, MFA) står i Supabase RLS policy-eksempler, testet via API-et.
En detalj fra den hemmelige nøkkelen: en rad den satte inn, fikk user_id = null, fordi auth.uid() ikke har noen bruker bak den nøkkelen. RLS ble hoppet over, men standardverdien gjorde ikke det kolonnenavnet tilsier.
Hva leste den offentlige nøkkelen etter hvert trinn?
To trinn lekker alt: en grant uten RLS, og en vanlig view over en tabell med riktige policyer. De neste to avsnittene forklarer begge.
Trenger du fortsatt RLS etter grant-endringen 30. oktober?
Ja. Siden 30. oktober 2026 gir Supabase ikke lenger nye tabeller i public automatiske grants til API-rollene, verken på nye eller eksisterende prosjekter. Min lokale stack oppfører seg allerede slik. En nylaget tabell med RLS av svarte 42501 til hver rolle, også den hemmelige nøkkelen, så ingenting lekket. Detaljene står i Supabase 30. oktober: hva som knekker og hvordan sjekke.
Men granten kommer tilbake så snart appen trenger tabellen, og fra da av er RLS det eneste som står mellom den offentlige nøkkelen og hver rad. Så legg alt i én migrering, i denne rekkefølgen:
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;
Gi grants bare til rollene som trenger tabellen. Da jeg trakk tilbake alt fra anon, endret den offentlige nøkkelens select seg fra 200 [] til 401, 42501. Granten er den første låsen, RLS den andre.
Hvorfor lekker en view fortsatt rader?
Fordi en view kjører med eierens rettigheter, og i Supabase er det postgres, som omgår RLS. Med policyene for egne rader på plass på todos ga en vanlig create view public.todos_view as select * from public.todos alle 3 rader til den offentlige nøkkelen og til bruker B.
Laget på nytt with (security_invoker = true) ga den samme viewen 0 rader til den offentlige nøkkelen, 2 til A og 1 til B, nøyaktig som tabellen:
create view public.todos_view with (security_invoker = true) as
select * from public.todos;
Supabases Advisor flagger den vanlige typen som security_definer_view; hva det betyr og hvordan du fikser det, står i notatet om Supabase security definer view.
Hvordan aktiverer du RLS på hver ny tabell automatisk?
Med en event trigger. Supabases guide til event triggers gir en funksjon rls_auto_enable() og en trigger ensure_rls som kjører etter create table, create table as og select into i public. Jeg installerte den slik den er publisert og fikk:
- En tabell laget etterpå med
create tablehadde RLS på uten noenalter. Den offentlige nøkkelen fikk200 []på select og401på insert. - En tabell laget med
create table as selecthadde også RLS på. - En tabell laget før triggeren forble av. Triggeren virker ikke tilbakevirkende.
Uten triggeren hadde en andre tabell, laget ved siden av den beskyttede, RLS av, og når den fikk en grant, kunne den offentlige nøkkelen lese og skrive den fritt. Den dekker dessuten bare public; andre eksponerte skjemaer trenger sin egen linje i funksjonen.
Hjelper FORCE ROW LEVEL SECURITY?
Ikke for rollene du bekymrer deg for. alter table ... force row level security får RLS til å gjelde også tabellens eier. I Supabase har både postgres og service_role BYPASSRLS, så etter force talte eieren fortsatt 3 rader, den hemmelige nøkkelen så fortsatt alt, og en view eid av postgres lekket fortsatt. Det endret bare noe for en eierrolle jeg laget uten BYPASSRLS: 2 rader før, 0 etter.
Hvordan sjekker du ditt eget prosjekt?
- List i SQL-editoren tabellene i
publicmed RLS av:
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 i dashbordet rapporterer det samme som
rls_disabled_in_public; hva den feilen betyr, står i notatet om RLS disabled in public. - Den gratis Supabase security check leser prosjektet ditt skrivebeskyttet og lister tabeller med RLS av som API-et kan nå, views som omgår RLS og policyer som slipper inn alle, med SQL for å fikse hver.
- En skanner kan ikke vite hvilke rader som tilhører hvem. For å bevise det må du sende forespørsler som to brukere, som ovenfor. Hele metoden for en app med roller står i Test Supabase RLS: 30 av 87 tilganger gjorde ingenting.
Slik testet jeg
- Supabase CLI 2.109.1 på en bærbar: Postgres 17.6, PostgREST 14.14, Auth 2.192.0. To brukere logget inn med passord, pluss de lokale publishable og secret keys.
- Én tabell,
public.todos, tilbakestilt til de samme 3 radene før hver forespørsel. Hvert trinn ble utført som SQL, og hver forespørsel gikk over REST-API-et, med statuskode og body notert. - Grants for trinn 2 til 5 ble lagt til for hånd, for å tilsvare et prosjekt laget før 30. oktober 2026.
Begrensninger. Én lokal stack, oppdiktede data, 28. oktober 2026. Dashbord-trinnene er sitert fra Supabases dokumentasjon, ikke klikket gjennom. Tabeller laget av dashbordets egen rolle kan få andre standard grants. Det testet jeg ikke, og heller ikke Storage, Realtime eller GraphQL.
Usikker på om tabellene dine er låst?
Jeg ser gjerne på et skjema sammen med deg. Uten salgstale.
Ta kontakt