Início / RLS / Mudança de grants de 30/10 · Read in English
Supabase RLS · Mudança na plataforma
Mudança de grants do Supabase em 30/10: o que quebra e a correção que vaza
A partir de 30/10, uma tabela nova em public fica inacessível pra anon / authenticated até você dar GRANT. Tabelas que já existem continuam funcionando. Coloque grant + RLS + policies na mesma migration. Não resolva o 42501 com grant ... on all tables: isso libera também toda tabela que está com RLS desligado.
01 O que muda em 30 de outubro
Até agora, toda tabela criada em public recebia automaticamente privilégios completos (select / insert / update / delete e outros) pra anon, authenticated e service_role, via default privileges. Em 30 de outubro de 2026 o Supabase desliga isso em todos os projetos existentes (projetos novos já vêm assim desde um rollout gradual que começou em 30 de maio). A partir daí:
Tabelas novas
Criadas sem GRANT, ficam invisíveis pra Data API (supabase-js, PostgREST, GraphQL), não importa como foram criadas: SQL editor, migration, CLI, MCP ou ferramenta de IA.
Tabelas existentes
Não mudam. Continuam com os grants que têm hoje, então o app que está no ar segue funcionando.
RLS e policies
Iguais. O grant decide se um role pode tocar na tabela; o RLS decide quais linhas.
Conexões diretas ao Postgres (psql, ORM, servidor com connection string) não são afetadas. Fonte: anúncio oficial do Supabase (em inglês).
02 Como é o "quebrou"
A primeira tabela nova depois da virada devolve isso pela API, em vez das linhas:
resposta (exemplo do anúncio do Supabase)
{
"code": "42501",
"message": "permission denied for table invoices",
"hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.invoices TO anon;"
}
O Postgres recusa a query antes mesmo de o RLS rodar. É o novo padrão funcionando como deveria, não um bug nas suas policies.
03 A correção que vaza
O jeito mais rápido de sumir com o erro é o grant em massa da seção de rollback do anúncio:
não cole isso como correção
grant select, insert, update, delete on all tables in schema public to anon, authenticated, service_role;
Funciona, e também libera todas as tabelas de public, inclusive as que você nunca quis expor. Qualquer uma delas com RLS desligado passa a poder ser lida e escrita por quem tiver sua anon key, que vai junto no seu frontend. O novo padrão era a chance de parar de expor tabela sem querer; o grant em massa devolve o problema. Peça pra uma ferramenta de IA "corrigir o erro de permissão" e essa linha é uma resposta bem provável.
Dê grant por tabela, e só o que cada role precisa.
04 O padrão de migration que funciona
Trate grant, RLS e policies como uma coisa só, na mesma migration, pra tabela nunca existir pela metade:
migration.sql
create table public.invoices ( id bigint generated always as identity primary key, user_id uuid not null references auth.users(id), amount numeric not null ); alter table public.invoices enable row level security; create policy "owner reads" on public.invoices for select to authenticated using (user_id = (select auth.uid())); -- só o role e as operações que o app realmente usa grant select on public.invoices to authenticated;
Sem grant pra anon aqui, porque visitante deslogado não lê fatura. Se a tabela usa serial / bigserial em vez de identity e a API insere nela, a sequence precisa do próprio grant: grant usage, select on sequence public.invoices_id_seq to authenticated;
05 Veja o que os roles da API alcançam hoje
Rode isso no SQL editor. Lista toda tabela e view de public, se anon ou authenticated conseguem ler, e se o RLS está ligado:
check.sql
select c.relname, has_any_column_privilege('anon', c.oid, 'select') as anon_can_read, has_any_column_privilege('authenticated', c.oid, 'select') as auth_can_read, c.relrowsecurity as rls_on from pg_class c join pg_namespace n on n.oid = c.relnamespace where n.nspname = 'public' and c.relkind in ('r', 'p', 'v', 'm') order by 1;
Tabela com anon_can_read = true e rls_on = false pode ser lida por qualquer um com a sua anon key. Dois detalhes deixam essa consulta mais rigorosa que a versão comum baseada em information_schema.table_privileges:
Grants de coluna
grant select (id, email) on profiles to anon não aparece como privilégio de tabela, mas a API continua servindo essas colunas. has_any_column_privilege pega grant de tabela e de coluna.
Views
Views também aparecem na API e, por padrão, leem as tabelas como o dono da view, pulando o RLS. Crie views com security_invoker = on pra elas respeitarem o RLS de quem chama.
06 Pra não voltar a acontecer
O novo padrão só te protege enquanto toda migration estiver certa. Dois checks grátis ajudam:
No CI
npx airlock-migrate lê seus arquivos de migration e quebra o build quando um deles cria tabela sem ligar o RLS, ou desliga o RLS. São justamente as tabelas que um grant em massa exporia. Também avisa sobre view sem security_invoker = on. Ele não aponta os grants em si, e só enxerga tabelas que passam pelos seus arquivos de migration.
No banco em produção
SUPABASE_DB_URL=... npx airlock-rls confere o banco rodando atrás de tabelas com RLS desligado e policies que deixam todo mundo passar. Configure no CI (em inglês).
Pegue isso antes de ir pro ar
airlock-rls é um gate de CI que quebra o build quando uma tabela vai pro ar exposta ou uma policy é permissiva: o mesmo tipo de bug, pego no CI contra um banco com as suas migrations aplicadas, antes de chegar em produção.
Ou comece pelo nextjs-supabase-starter: auth + uma tabela com RLS + um teste de isolamento, pra começar de um padrão que já vem selado.
Baixe o cheat sheet de RLS do Supabase
As regras de ouro, as armadilhas que vazam em produção, policies corretas e o teste de isolamento, numa página só (em inglês).
FAQ
Minhas tabelas atuais param de funcionar em 30 de outubro?
Não. O Supabase só muda o padrão das tabelas criadas depois da virada. As tabelas que já existem mantêm os grants atuais. O primeiro a quebrar é a próxima tabela nova criada sem GRANT.
Essa mudança deixa meu app mais seguro sozinha?
Só pras tabelas novas. Toda tabela existente mantém os grants antigos, então uma que está com RLS desligado hoje continua legível pela API depois de 30/10. Rode o check acima e ligue o RLS em tudo que aparecer com anon_can_read true e rls_on false.
Dou grant pra anon ou pra authenticated?
Pro role que realmente precisa da tabela. Dado que só usuário logado vê recebe grant pra authenticated e nenhum pra anon. Dado público, como um catálogo de produtos, pode ter grant pra anon com uma policy de RLS que permita a leitura.
O service_role também é afetado?
Sim. Ele também perde os grants automáticos em tabelas novas, então um backend usando a chave service_role recebe 42501 numa tabela nova até você dar o grant. Depois do grant, ele continua ignorando o RLS.