Noções de segurança
Supabase "RLS disabled in public": o que o aviso não vê
O Supabase marca "RLS disabled in public" como erro. Ele não diz nada sobre a política de leitura que deixa a sua tabela igualmente aberta.

Em resumo
- "RLS disabled in public" quer dizer uma coisa só: uma tabela do seu schema public está com o Row Level Security desligado, então qualquer pessoa com o endereço do seu projeto e a sua chave publicável consegue ler.
- Ele não diz nada sobre uma tabela onde você ligou a chave e depois escreveu uma política que deixa todo mundo ler. Existe uma regra para políticas sempre verdadeiras, e ela pula as políticas de leitura de propósito.
- Resolva as tabelas apontadas e depois confira o resto por fora, porque o advisor lê as suas configurações e nunca pergunta ao seu banco o que um desconhecido recebe de verdade.
Você abriu o Security Advisor no seu dashboard do Supabase, ou alguém colou a saída dele na sua frente, e lá está em vermelho: RLS disabled in public. Embaixo, uma linha por tabela, nas palavras do próprio Supabase:
Table public.profiles is public, but RLS has not been enabled.
Aqui está a parte que guia após guia conta errado: zerar essa lista não quer dizer que ninguém consegue ler os seus dados. O advisor tem uma regra separada para uma política que deixa todo mundo entrar, e essa regra pula exatamente a forma que um construtor de IA escreve quando destrava o seu app. Então a lista que você acabou de resolver e a lista de tabelas que um desconhecido consegue ler são duas listas diferentes, e uma delas não aparece em lugar nenhum do seu dashboard.
O que significa "RLS disabled in public"?
Uma tabela do seu schema public está com o Row Level Security desligado, e a consequência é que qualquer pessoa com o endereço do seu projeto consegue ler todas as linhas dela.
As duas metades disso precisam ser abertas. O schema public é o lugar para onde uma tabela vai quando ninguém diz o contrário, e é a parte do seu banco que o Supabase publica na web: cada projeto responde requisições em um endereço próprio, e a chave necessária para falar com ele está no código que o seu site manda para cada visitante. Row Level Security é a chave que decide se as suas regras são consultadas antes de as linhas saírem. Com ela desligada não há nada para consultar, então a resposta é sempre sim.
É essa combinação que faz isso ser reportado como erro e não como aviso. O advisor classifica o que encontra, e este é o degrau mais alto.
Pense no advisor como um fiscal com uma prancheta. Ele lê a sua papelada com cuidado e faz isso bem. Ele nunca testa a maçaneta. Nada naquele painel é resultado de uma requisição que alguém tenha feito ao seu banco.
Ativar o RLS vai quebrar o meu app?
Vai, na hora, e é a configuração funcionando.
A correção que o Supabase te dá é uma linha, e você roda ela no SQL Editor:
alter table public.profiles enable row level security;
A documentação do próprio Supabase é direta sobre o que vem depois: com uma chave publicável os dados ficam inacessíveis pela API enquanto não houver políticas definidas. Então suas listas voltam vazias, suas telas ficam em branco, e o erro no advisor é substituído por uma entrada mais discreta dizendo que a tabela está com RLS ativo mas sem políticas.
Essa é a porta fechada com ninguém ainda na lista. A próxima coisa que quase todo mundo faz é adicionar uma política que deixa todo mundo ler, porque é o que faz as telas voltarem.
A falha que o aviso não está procurando
Uma tabela com o Row Level Security ligado e uma política de leitura cuja
condição é USING (true) entrega exatamente as mesmas linhas exatamente para o
mesmo desconhecido. O advisor não aponta isso.
Não é um descuido. O Supabase tem sim uma regra para políticas sempre verdadeiras, e ela deixa as de leitura de fora de propósito. A descrição da própria regra diz isso:
SELECT policies with `USING (true)` are intentionally excluded as this
pattern is often used deliberately for public read access.
No caso geral a regra está certa. Um catálogo de produtos, uma lista de artigos publicados, um mapa de locais: essas coisas foram feitas para qualquer um ler, e apontá-las treinaria todo desenvolvedor da plataforma a ignorar o painel. O que nenhum linter consegue saber é se a tabela na frente dele guarda locais ou clientes.
E uma política que deixa todo mundo ler é o jeito mais rápido de fazer um app
quebrado voltar a funcionar, que é por isso que um construtor de IA recorre a
ela. Peça para o Cursor ou para o Lovable consertar as telas vazias e
FOR SELECT USING (true) é uma resposta comum. O seu app carrega, o erro some,
o painel se cala, e a tabela continua tão legível quanto antes.
| O que o advisor consegue ver | Como ele reporta | O que um desconhecido com a sua chave publicável recebe |
|---|---|---|
| RLS desligado | Erro | Todas as linhas |
| RLS ligado, nenhuma política | Info | Nada |
RLS ligado, FOR SELECT USING (true) | Nada | Todas as linhas |
RLS ligado, FOR ALL USING (true) | Aviso | Todas as linhas, e pode mudá-las |
RLS ligado, USING (auth.uid() = user_id) | Nada | Só as dele |
As duas linhas onde nada é reportado são o par que merece um minuto. Uma é uma tabela que ninguém fora do seu app consegue tocar. A outra é uma tabela que qualquer um consegue ler. O seu dashboard fica igualmente calado sobre as duas.
Como essa política foi parar ali, e por o que trocá-la tabela por tabela, é o assunto de o Row Level Security está ligado e a tabela continua pública. Se a mesma política também precisa deixar o seu app gravar, o erro que você encontra em seguida é new row violates row-level security policy.
Por que a tabela que você criou com uma migração nunca te avisou
Porque o Table Editor liga o Row Level Security para você e o SQL não liga.
O Supabase documenta a diferença sem rodeios: tabelas criadas com o Table Editor
do dashboard vêm com RLS ativo por padrão, e tabelas criadas com SQL puro
precisam ter isso ativado explicitamente. Uma tabela que você criou clicando
começa protegida. Uma tabela que chegou por um arquivo de migração, um
supabase db push, um trecho no SQL Editor ou um comando que o seu construtor
de IA rodou por você começa aberta.
Esse segundo caminho é como um construtor de IA cria tabelas. Ele escreve o SQL e roda por você, então você nunca viu a caixinha e nunca viu que ela estava desmarcada.
O hábito que fecha isso é colocar a linha na migração, do lado da coisa que ela protege:
create table public.profiles (
id uuid primary key references auth.users,
full_name text
);
alter table public.profiles enable row level security;
Como conferir as tabelas que o advisor liberou
Faça ao seu banco a pergunta que um desconhecido faz: mande uma requisição de fora, com a chave publicável que viaja dentro do seu app, e veja o que volta.
Essa é a diferença em torno da qual o artigo inteiro gira. O advisor lê a sua configuração. Uma requisição lê as suas linhas. São duas perguntas diferentes, e uma tabela pode passar na primeira e não passar na segunda, que é exatamente o que uma política de leitura permissiva faz.
Nós fizemos essa requisição em escala. Entre 12 e 14 de agosto de 2026 rodamos nove verificações externas em 30.998 apps no ar publicados pelo Lovable, Base44, Replit, v0 e Bolt. Dos 3.680 apps com Supabase onde a verificação conseguiu terminar, 2.096 tinham pelo menos uma tabela que respondeu a uma requisição anônima com linhas. Isso dá 57%, e é uma fatia dos apps de que conseguimos uma resposta clara, não de tudo que escaneamos. Entre os apps feitos com Bolt foram 27 de 35, uma amostra pequena o bastante para ler como direção e não como taxa. O conjunto de dados completo está publicado, e de que esses 57% são uma fatia percorre a contagem.
Você mesmo pode fazer essa requisição em uma tabela com um navegador e a sua chave publicável. Se preferir não ir tabela por tabela, o nosso escaneamento gratuito pergunta ao seu app no ar por fora e diz quais tabelas responderam. Leva uns 20 segundos e não precisa de conta: escanear seu app.
Preciso de um backup antes de mudar políticas de RLS?
Para qualquer tabela em que o seu app grava, sim. Para uma tabela só de leitura que você está apenas apertando, o risco é o seu app ficar em branco, não os seus dados sumirem.
Vale separar duas coisas diferentes aqui, porque só uma delas tem a ver com o conserto.
O que uma política permissiva já permitiu. Se a regra na tabela era
FOR ALL USING (true) e não FOR SELECT, então quem a encontrasse podia mudar
e apagar linhas além de ler, e apertá-la hoje não faz nada em relação a ontem.
Essa versão costuma aparecer como uma mensagem no suporte sobre dados que
mudaram sozinhos, ou como uma tabela que de repente está vazia.
O conserto em si. Reescrever políticas em uma dúzia de tabelas é uma mudança em um banco no ar, escrita pelas mesmas ferramentas que produziram o problema. Uma migração que apaga uma política e recria torto é uma terça-feira comum, e o caminho de volta é uma cópia de como as coisas estavam uma hora atrás.
Em um plano pago do Supabase a cópia de ontem à noite está ali no console. No plano gratuito não há nada para onde voltar, porque o plano gratuito não faz backup automático nenhum. Se for o seu caso, tire uma antes de encostar em uma política: como fazer backup de um banco Supabase no plano gratuito é a versão de dez minutos.
Do que o Reeve Care guarda uma cópia
Do seu banco Supabase, copiado em uma programação, guardado fora da sua conta do Supabase, criptografado e lido de volta antes de a data no seu dashboard mudar. Os arquivos que os seus usuários subiram viajam junto assim que você conecta uma chave de Storage.
Dois limites, ditos de antemão. Os backups são só do Supabase: se os seus dados moram em outro lugar, a gente fala, em vez de te vender uma assinatura que vigia uma caixa vazia. E a chave de Storage é pedida à parte, porque o Supabase não emite chave só de leitura para arquivos, então a que copia os seus uploads também consegue gravar. A que copia o seu banco não consegue. Conectar é opcional, e o banco é salvo de qualquer jeito.
A restauração é a parte que importa para este artigo. Colocar uma cópia antiga por cima de um banco no ar é o botão mais assustador do produto, então o Care tira primeiro uma cópia do estado atual e só depois reproduz a que você escolheu. A restauração tem um desfazer próprio.
A outra metade do Care é aquela para a qual este artigo fica apontando. Uma política que afrouxou durante uma migração não é coisa que você acha olhando, então a mesma verificação externa roda de novo em uma programação e te avisa quando a resposta muda. Uptime, um relatório mensal e o escaneamento estão na mesma assinatura.
O que uma cópia não faz é escrever as suas políticas, e nenhum backup deixa fechada uma tabela aberta. Essas tabelas continuam sendo trabalho seu. O que a cópia muda é o que acontece quando o conserto sai torto. O que o Reeve salva no Supabase, com que frequência, e o que uma restauração faz desenha o ciclo inteiro, e os planos e os preços deles estão na página de preços.
O que fazer esta semana
O que fazer
- Zere primeiro as entradas "RLS disabled in public". São as tabelas onde nada é consultado, e a correção é uma linha
alter tableem cada uma. - Depois abra Authentication → Policies e leia a condição de cada política que sobrou. Um
USING (true)em uma política de leitura é invisível para o advisor e escancarado para um desconhecido. - Decida tabela por tabela se você ficaria à vontade publicando o conteúdo dela em uma página. Essa é a pergunta que o linter não consegue responder por você, e com uma política de leitura permissiva é a única que importa.
- Coloque
alter table ... enable row level security;em toda migração que cria uma tabela, e rode o advisor de novo depois de cada uma. O padrão do dashboard só vale para as tabelas que você cria clicando. - Tire uma cópia do seu banco antes de reescrever políticas em uma tabela no ar, e veja em qual plano do Supabase você está para saber se já tem uma.
Comece pela tabela que mais te deixaria sem graça como página pública. O checklist de segurança em 10 minutos cobre isso junto com o resto do que vale conferir em um app recém-lançado, e o guia de segurança do Supabase passa pelo que mais costuma ficar aberto.
Perguntas frequentes
"RLS disabled in public" é um erro ou um aviso?
Um erro, e é o nível mais alto que o advisor usa. A entrada diz "Table public.<name> is public, but RLS has not been enabled." As duas entradas vizinhas são mais discretas: uma tabela com a chave ligada e nenhuma política é reportada como INFO, e uma política cuja condição é sempre verdadeira é reportada como WARN. Esses níveis dizem o quanto o linter confia no que consegue enxergar na sua configuração, não o tamanho do seu problema.
Ativar o Row Level Security vai quebrar o meu app?
Na hora, sim, e é a configuração fazendo o trabalho dela. O Supabase documenta que os dados ficam inacessíveis pela API com uma chave publicável enquanto não houver políticas definidas, então no momento em que você roda a linha suas listas voltam vazias e suas telas ficam em branco. O app volta quando você adiciona uma política dizendo quem pode ver quais linhas. O erro a evitar é adicionar uma que libera todo mundo, porque essa versão também faz o app funcionar.
Ativei o RLS e agora nada carrega. O que aconteceu?
Nada quebrou. Com o Row Level Security ligado e nenhuma política escrita, o Postgres (o motor de banco de dados por baixo do Supabase) recusa todas as requisições, inclusive as do seu próprio app, e o advisor troca o erro dele por uma entrada INFO dizendo que a tabela está com RLS ativo mas sem políticas. Escreva uma política para as linhas que o seu app deve mostrar, começando pela que compara o visitante logado com a coluna de dono da linha.
Minha tabela não foi apontada e mesmo assim qualquer um consegue ler. Por quê?
O mais provável é que o Row Level Security esteja ligado e exista uma política de leitura cuja condição é USING (true). O advisor tem sim uma regra para políticas sempre verdadeiras, e ela pula as de leitura de propósito, porque acesso público de leitura é uma coisa razoável para um catálogo de produtos ou uma lista de artigos publicados. Nada no seu dashboard sabe se a sua tabela guarda locais ou clientes, então essa conferência é sua.
Preciso de Row Level Security se o meu app só conversa com o meu servidor?
Se o navegador realmente nunca conversa com o Supabase e nenhuma chave publicável está no código que o seu site manda para os visitantes, então a Data API não é uma porta de entrada e não são as políticas que protegem essas tabelas. Isso é raro em um app feito com Lovable, Bolt ou v0, porque esses construtores ligam o navegador direto no Supabase por padrão. Abra o seu próprio site, veja se a URL do projeto e a chave publicável estão na página, e deixe essa resposta decidir.