Guia · Supabase
Supabase RLS: como saber se o seu banco está aberto — e fechar
No Supabase, a chave que vai no seu site (a anon) é pública de propósito. Quem decide o que ela consegue ler é o Row Level Security (RLS). Tabela sem RLS, ou com uma regra que deixa todo mundo passar, responde a qualquer pessoa que tenha essa chave — ou seja, a qualquer visitante.
O que é o RLS, em uma frase
Row Level Security é a regra, escrita no próprio banco, que decide quais linhas de uma tabela cada pessoa pode ler ou alterar.
Ela importa tanto no Supabase porque o seu site conversa com o banco direto do navegador, usando a chave anon — que fica no JavaScript, à vista de qualquer um. Isso é de propósito, não é erro: a anon foi feita para ser pública. O que separa “pública” de “aberta” é o RLS.
Os quatro estados de uma tabela
| Estado | O que a chave anon consegue | Na prática |
|---|---|---|
| RLS desligado | Ler tudo (e alterar, conforme as permissões da tabela) | Aberta |
RLS ligado, regra using (true) | Todo mundo passa pela regra | Aberta, com cara de fechada |
| RLS ligado, regras por usuário | Cada pessoa alcança só as próprias linhas | É o objetivo |
| RLS ligado, nenhuma regra | Ninguém lê pela API | Fechada — mas o app pode parar de funcionar |
Como verificar
No painel do Supabase
- O editor de tabelas sinaliza as tabelas que estão sem RLS.
- As recomendações de segurança (Security Advisor) listam as tabelas públicas sem regra de acesso.
- No editor de SQL, estas duas consultas mostram o quadro completo:
-- quais tabelas do schema public estão SEM RLS
select tablename, rowsecurity
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;
-- quais regras existem, e o que cada uma deixa passar
select tablename, policyname, cmd, qual, with_check
from pg_policies
where schemaname = 'public'
order by tablename;Na primeira, rowsecurity = false é tabela sem RLS. Na segunda, uma regra com qual = true deixa todo mundo passar.
De fora, como um visitante
O teste que decide é perguntar à API o que ela entrega a quem não fez login. Com a URL do projeto e a chave anon — as duas estão no seu próprio site:
curl -I "https://SEU-PROJETO.supabase.co/rest/v1/NOME_DA_TABELA?select=*" \
-H "apikey: SUA_CHAVE_ANON" \
-H "Prefer: count=exact"O -I pede só o cabeçalho: nenhuma linha é baixada. Se a resposta vier com um Content-Range terminando num número maior que zero — por exemplo 0-24/318 —, a tabela está entregando registros a quem não fez login. Se vier */0, a regra barrou (ou a tabela está vazia).
Só no seu projeto
Faça esse teste apenas em um projeto que é seu ou que você tem autorização para verificar.
Como fechar
O modelo mais comum é “cada pessoa vê o que é dela”. Troque pedidos e user_id pelos nomes da sua tabela e da coluna que guarda o dono da linha:
-- 1. ligar o RLS na tabela
alter table public.pedidos enable row level security;
-- 2. cada pessoa só lê os próprios pedidos
create policy "ler os próprios pedidos"
on public.pedidos for select
to authenticated
using ((select auth.uid()) = user_id);
-- 3. só cria pedido em nome de si mesma
create policy "criar os próprios pedidos"
on public.pedidos for insert
to authenticated
with check ((select auth.uid()) = user_id);
-- 4. alterar e apagar seguem a mesma regra
create policy "alterar os próprios pedidos"
on public.pedidos for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);
create policy "apagar os próprios pedidos"
on public.pedidos for delete
to authenticated
using ((select auth.uid()) = user_id);O (select auth.uid()) entre parênteses é a forma que a documentação do Supabase recomenda, por desempenho. Tabelas de conteúdo público — um catálogo, por exemplo — podem ter só a regra de leitura para todos, sem as de escrita.
Armadilhas comuns
using (true)parece regra, mas libera todo mundo.- Views: uma view sobre uma tabela com RLS pode ignorar a regra, porque por padrão roda com as permissões de quem a criou. No Postgres 15 ou mais novo, crie com
with (security_invoker = true). - Funções
security definerexpostas pela API rodam com as permissões do dono, não de quem chama. - Arquivos (Storage) têm regras próprias: bucket público entrega qualquer arquivo a quem tiver o endereço.
- A chave service_role ignora todo o RLS — ela nunca vai no front-end. Veja chave de API exposta.
Prompt para colar no Lovable, Bolt ou Cursor
Ative Row Level Security em todas as tabelas do schema public do Supabase.
Para cada tabela com dados de usuário, crie políticas de SELECT, INSERT,
UPDATE e DELETE que permitam acesso apenas às linhas em que
user_id = (select auth.uid()). Não use "using (true)" em nenhuma tabela
com dados de usuário. Se alguma view expõe essas tabelas, recrie-a com
security_invoker = true. No fim, liste cada tabela e suas políticas.