Noções de segurança
Como rotacionar uma chave service_role do Supabase vazada
O Supabase manda fechar o vazamento primeiro. Outros guias mandam rotacionar agora. O que vale depende de para onde a sua chave service_role vazou.

Em resumo
- Se a sua chave service_role do Supabase está no JavaScript que um navegador baixa, rotacione antes de consertar qualquer coisa. A cópia que já saiu não expira.
- Se ela só chegou a um repositório privado ou a um log, feche a origem primeiro, ou a chave nova vai sair atrás da antiga no próximo deploy.
- Uma chave service_role antiga não pode ser rotacionada. Invalidar uma delas significa criar as novas chaves sb_secret_, mover o app para elas e desativar o par antigo com um botão que vale para os dois.
Alguém disse que a sua chave service_role do Supabase está no seu app, onde
qualquer um consegue ler. Antes de mudar qualquer coisa, tenha certeza de que é
essa mesma a chave que encontraram: nosso scan gratuito lê o seu site
no ar do jeito que um estranho leria e diz qual chave do Supabase ele realmente
enxerga ali. Sem conta e sem instalar nada.
O conselho que vem depois de um achado desses é quase sempre uma palavra só: rotacione. Aí você vai procurar como se faz, e as fontes se contradizem. A própria documentação do Supabase abre o guia de rotação mandando corrigir a causa do vazamento antes de começar. Meia dúzia de guias de terceiros manda rotacionar na hora e perguntar depois.
Esta é a parte que nenhum dos dois escreve: os dois estão certos, para situações diferentes, e a pergunta que separa uma da outra é para onde a chave vazou.
Uma coisa para deixar clara antes de tudo. Apagar a chave do seu código tira a sua própria cópia do chaveiro. Rotacionar troca a fechadura. Só a segunda alcança as cópias que outras pessoas já têm.
É mesmo a chave service_role?
Num projeto novo os primeiros caracteres respondem. sb_secret_ no começo é a
secreta. sb_publishable_ é a chave que deve estar no seu app, e encontrar ela
ali não é achado nenhum.
Tenha certeza antes de fazer qualquer coisa drástica, porque a chave que aparece num app feito com vibe coding costuma ser justamente a que pertence àquele lugar.
Projetos mais antigos entregam outro par, anon e service_role, e as duas são
quase idênticas: mesmo formato, mesmo tamanho, nenhum prefixo para ler. A seção
do meio de uma dessas chaves não é criptografada. Ela decodifica em algumas
linhas de texto simples, e uma delas informa o papel.
Quais chaves de API são seguras no frontend
percorre as duas verificações.
O motivo para ter certeza é que isso é mais raro do que os avisos sugerem.
Escaneamos 30.998 apps feitos com vibe coding em agosto de 2026 e encontramos
uma chave service_role publicada em 3 deles. Uma chave de API do Google, que
costuma ser inofensiva e muitas vezes fica restrita a um domínio, apareceu em
1.142. A contagem completa está aqui.
Rotacionar primeiro ou fechar o vazamento primeiro?
Depende de a chave já ser pública, e a linha entre os dois casos é limpa.
Se a chave está no JavaScript que os seus visitantes baixam, ela já é pública. Qualquer pessoa que carregou o seu site enquanto ela estava ativa tem uma cópia, e todo crawler automático que estava atrás exatamente dessa sequência também tem. Nada do que você mudar no código alcança essas cópias. Rotacione primeiro, depois feche a origem.
Se ela só chegou a um repositório privado, um arquivo de log, uma variável de CI ou uma conversa, a exposição é limitada. Se aqui você rotacionar primeiro, vai fazer isso duas vezes, porque o próximo deploy empurra o valor antigo de volta para fora de onde quer que ele venha e a chave nova sai atrás da antiga. Feche a origem e depois rotacione.
O guia do Supabase foi escrito para o segundo caso. Os guias apressados foram escritos para o primeiro. Se você está lendo isto porque um scanner achou a chave na sua URL no ar, você está no primeiro caso.
Como rotacionar uma chave secreta do Supabase
Se o seu projeto tem chaves sb_secret_, isso se resolve no painel e sem
indisponibilidade.
Um projeto pode manter mais de uma chave secreta ao mesmo tempo, cada uma com o seu nome, e é isso que deixa a operação segura: você adiciona a nova antes de tirar a antiga, então no meio do caminho nada fica quebrado.
- Abra o projeto, vá em Settings → API Keys e crie uma chave secreta nova.
- Coloque ela em todo lugar onde a antiga era usada, e tudo isso deveria estar num servidor: edge functions, webhooks, jobs agendados, um backend seu. Onde esses valores ficam se você nunca abriu essa página.
- Faça o deploy e depois passe pelas partes do app que leem e escrevem dados. Uma chave secreta errada falha alto e na hora, que é o caso bom.
- Só então apague a chave comprometida.
O passo 4 é permanente. O Supabase apaga uma chave secreta de verdade: sem desfazer, sem religar, sem cópia guardada que você possa buscar. É por isso que ele vem por último.
Apagar uma chave secreta não desloga os seus usuários. Uma chave de API diz qual aplicação está chamando o seu banco de dados, e o token de sessão de um visitante diz qual usuário ele é. O segundo é conferido contra a chave de assinatura do projeto, e esse é um valor separado que você não encostou.
Por que uma chave service_role antiga do Supabase não pode ser rotacionada
Porque não existe botão para isso. A nota de solução de problemas do Supabase
diz que a rotação direta dos segredos antigos anon, service_role e JWT não é
mais suportada, e aponta para as chaves novas.
Então invalidar uma chave service_role antiga que vazou é uma migração e não
uma rotação:
- Crie as chaves novas. Isso acrescenta
sb_publishable_esb_secret_ao lado do par antigo. Os dois sistemas funcionam ao mesmo tempo e nada quebra. - Mova o seu frontend para a chave publicável. No Lovable ou no Bolt isso costuma ser um valor nas configurações do projeto e não uma linha de código.
- Mova todo uso do lado do servidor para a chave secreta.
- Desative as chaves antigas nas configurações do projeto.
O passo 4 é um botão só e ele vale para as duas chaves antigas. anon e
service_role são JWTs assinados pelo mesmo segredo, então o botão que revoga
uma revoga a outra, e um frontend que ainda carrega a chave anon antiga para
de funcionar no instante em que você vira isso. É por isso que o passo 2 vem
antes.
Desativar tem volta, e essa é a única boa notícia desta parte: se algo que você esqueceu ainda estiver usando uma chave antiga, dá para ligar de novo enquanto conserta. O que a migração envolve está contado com mais calma lá.
De onde a chave vazou, e como fechar isso
Três causas explicam quase tudo, e as três estão dentro do seu próprio projeto.
Um prefixo VITE_ ou NEXT_PUBLIC_. Isso não é uma configuração de
segurança que alguém esqueceu de ligar. É uma instrução para o build: coloque
este valor no bundle. Uma variável chamada VITE_SUPABASE_SERVICE_ROLE_KEY foi
compilada no seu JavaScript de propósito, por uma ferramenta que fez exatamente
o que mandaram.
Um valor colado direto num componente. Nenhum prefixo envolvido e nenhum
arquivo .env, só a chave parada numa linha de código porque era o jeito mais
rápido de fazer uma consulta devolver alguma coisa.
Trabalho que pertence a um servidor. Apagar uma conta, escrever numa tabela onde os seus usuários não podem escrever, ler linhas de todo mundo. A chave foi puxada porque o navegador não dava conta do serviço, e a saída é mover o serviço: uma edge function, uma rota serverless, qualquer coisa que os seus visitantes não baixem.
Como eu sei se alguém usou?
Normalmente você não tem como ter certeza. O que dá para fazer é estreitar a janela e olhar dentro dela.
A janela abre com o deploy que publicou a chave pela primeira vez e fecha quando você a desativou. O seu projeto Supabase guarda logs da API e do banco, e é para esse intervalo que eles precisam ser filtrados. O que você procura são leituras e escritas que não consegue explicar: requisições a tabelas que o seu app nunca toca, tráfego em horários em que ninguém estava usando, exclusões que ninguém fez.
Depois confira os dados em si. Contagens de linhas contra o que você espera, os seus próprios registros de conta, qualquer coisa com um carimbo de data que se moveu enquanto ninguém trabalhava. Uma mensagem no suporte sobre dados que mudaram sozinhos é como a maior parte desses casos é descoberta de verdade, e ela chega semanas depois.
Uma chave service_role não alcança o seu meio de pagamento nem o seu envio de
e-mail. Ainda assim vale saber se ela era a única chave no seu bundle, porque as
que gastam dinheiro vazam pelo mesmo caminho:
o que 30.998 apps estavam publicando.
Confira o seu app no ar antes de dar por encerrado
Carregue o site numa janela anônima, abra o JavaScript que o navegador baixou e procure ali o valor antigo. Procure depois o novo, que também não deveria estar lá.
Duas coisas fazem valer a conferência na mão. Um build pode servir um bundle em cache por um tempo depois do deploy, então o arquivo que um visitante recebe nem sempre é o que você acabou de construir. E uma chave pode estar em mais de um lugar: um segundo ponto de entrada, um service worker, um build antigo ainda sendo servido de um caminho que ninguém aponta.
Nosso scan gratuito faz essa parte de fora, na URL que os seus visitantes usam de verdade, e decodifica uma chave do Supabase o suficiente para ler o papel, então uma chave publicável volta com um visto. Leva uns vinte segundos e não pede conta: escaneie o seu app.
O que fazer agora
O que fazer
- Confirme a chave primeiro.
sb_secret_na frente, ou"role": "service_role"dentro de uma mais antiga. Uma chave publicável no seu frontend não é achado. - Se ela está no bundle que os seus visitantes baixam, rotacione antes de mexer no código. Editar o app não alcança as cópias que já saíram.
- Se ela só vazou para um repositório, um log ou uma conversa, feche a origem primeiro, para o seu próximo deploy não empurrar a chave nova para fora na sequência.
- Num projeto novo: crie uma segunda chave secreta, mova o código do servidor para ela, faça o deploy e depois apague a antiga. Apagar é definitivo.
- Num projeto antigo não existe botão de rotação. Crie as chaves novas, mova o frontend e o servidor para elas e depois desative o par antigo com o único botão que vale para os dois.
- Procure no seu bundle no ar depois. Um build em cache pode servir o valor antigo por um tempo depois do deploy.
Uma chave service_role lê e escreve toda linha do seu banco de dados. A pior
versão dessa história termina com uma tabela vazia, e se essas linhas voltam
depende
do que você estava salvando
antes de tudo isso acontecer.
Perguntas frequentes
Rotacionar a chave quebra o meu app no ar?
Não, se você seguir a ordem. Num projeto com as chaves novas, você adiciona uma segunda chave secreta, move o código do seu servidor para ela, faz o deploy e só então apaga a antiga, então nunca existe um momento sem uma chave funcionando. Num projeto antigo o risco é outro: desativar o par antigo também desativa a chave anon que o seu frontend usa, então o app cai a menos que ele já esteja na chave publicável quando você virar essa chave.
Dá para rotacionar as chaves antigas anon e service_role?
Não. A documentação de solução de problemas do Supabase diz que a rotação direta dos segredos antigos anon, service_role e JWT não é mais suportada. Invalidar uma chave antiga vazada significa criar as novas chaves sb_publishable_ e sb_secret_, mover o seu app e o código do seu servidor para elas e depois desativar o par antigo nas configurações do projeto.
Apago a chave antiga ou desativo?
Você não escolhe, porque os dois sistemas de chave se comportam de formas diferentes. Uma chave sb_secret_ nova é apagada, em definitivo, sem nenhuma forma de trazer de volta. O par antigo é desativado, e desativar tem volta: se algo que você esqueceu ainda estiver usando uma chave antiga, dá para ligar de novo enquanto você conserta.
Como eu sei se alguém usou?
Normalmente você não tem como ter certeza. Reduza isso a uma janela: a chave ficou utilizável desde o deploy que a publicou pela primeira vez até o momento em que você a desativou. Filtre os logs da API e do banco do seu projeto Supabase para esse intervalo e procure leituras e escritas que você não consegue explicar; depois confira os dados em si, contagens de linhas e carimbos de data que não batem com nada que você tenha feito.
Preciso rotacionar a chave anon também?
Num projeto antigo você não tem escolha: desativar o par antigo leva as duas chaves de uma vez, então o seu frontend precisa da chave publicável nova antes de você virar o botão. Num projeto novo a chave publicável foi feita para ser pública e não há motivo para mexer nela. O que vale conferir nos dois casos é o Row Level Security, porque é a única coisa entre uma chave publicável e todo o seu banco de dados.