Noções de segurança
Por que o Supabase redireciona links de e-mail para localhost:3000
Se o Supabase redireciona para localhost:3000, a Site URL nunca foi trocada ou o seu endereço falta em Redirect URLs. A solução, e o curinga a evitar.

Em resumo
- Um redirecionamento do Supabase para localhost:3000 vem da Site URL da página URL Configuration. Um projeto começa com http://localhost:3000, e todo link de e-mail volta para ela quando o endereço que o seu app pediu não está na lista Redirect URLs.
- Quem apertou um link de cadastro normalmente já está confirmado. O Supabase confirma a conta antes de mandar a pessoa adiante, então ela pode entrar no seu site de verdade.
- Coloque a Site URL no seu endereço no ar e liste os endereços exatos que você usa. Um curinga tão amplo que cobre o app de outra pessoa deixa um link forjado mandar os seus usuários para lá, já com login feito.
Um usuário novo se cadastra no seu app no ar, abre o e-mail de confirmação e aperta o botão. O navegador dele abre uma página que diz "This site can't be reached" e "localhost refused to connect" (assim num Chrome em inglês), acima de um endereço que começa assim:
http://localhost:3000/#access_token=…&expires_at=…&expires_in=…&refresh_token=…&sb=&token_type=bearer&type=signup
Se o seu app usa o fluxo PKCE, ele termina em ?code=…. Você construiu o app
no Lovable, Bolt, v0, Cursor, Claude Code ou Replit, na pré-visualização ele
funcionava perfeitamente, e o login com senha continua funcionando.
Um redirecionamento do Supabase para localhost significa que o Supabase Auth, a
parte do Supabase que faz o login das pessoas, terminou o trabalho dele e
depois mandou a pessoa para a Site URL do seu projeto, que ainda diz
http://localhost:3000. Esse endereço quer dizer "este computador". No
computador do seu usuário não tem nada ali. A solução são duas configurações
numa página do painel do Supabase.
Esta é a parte que as soluções rápidas da internet erram: o curinga que faz essa mensagem sumir com uma linha é também o que deixa o link de um estranho mandar os seus usuários para a página dele, já com login feito. As configurações abaixo fazem os links funcionarem e mantêm essa porta fechada.
O que significa um redirecionamento do Supabase para localhost?
Significa que o Supabase mandou a pessoa para o endereço padrão do seu projeto, porque não aceitou um melhor.
Todo e-mail que o Supabase envia por você, seja uma confirmação de cadastro, um
magic link, uma redefinição de senha ou um convite, leva primeiro ao endereço
do seu próprio projeto. O
guia de modelos de e-mail
do Supabase mostra o formato:
https://project-ref.supabase.co/auth/v1/verify?token=…&type=email&redirect_to=https://example.com/path.
Ali o Supabase verifica o token e depois manda o navegador para o que
redirect_to indicar.
Pense nisso como uma placa de sinalização na porta do Supabase. Cada pessoa que
abre um link faz login na porta e depois segue, com a chave na mão, por uma das
setas. Quem coloca as setas é você. A Site URL é a seta que todo mundo pega
quando nenhuma outra serve, e num projeto novo ela aponta para
http://localhost:3000: a oficina onde o app foi construído, que na máquina de
qualquer outra pessoa é uma sala vazia.
O
guia de redirecionamentos
do Supabase diz que a Site URL define o redirecionamento padrão quando o código
não indica nenhum redirectTo, pede que você a troque de
http://localhost:3000 para o seu endereço de produção e chama essa
configuração de crítica para confirmações por e-mail e redefinições de senha.
Por que o Supabase ignora o endereço que o meu app pediu?
Porque esse endereço não está na sua lista Redirect URLs, e o Supabase recorre a outro sem avisar você.
O seu app pode dizer onde as pessoas devem chegar. Na biblioteca JavaScript do
Supabase isso é emailRedirectTo em signUp e signInWithOtp, e
redirectTo em resetPasswordForEmail e signInWithOAuth. Depois o Supabase
Auth decide em
uma função curta,
nesta ordem:
- O endereço que o seu app pediu, se ele bate com uma entrada da sua lista Redirect URLs ou fica no mesmo domínio da sua Site URL.
- O endereço da página de onde veio a requisição, do jeito que o navegador informou, pela mesma regra.
- Senão, a Site URL. O Supabase não dá erro nenhum, e o console do seu app não mostra nada.
Ele toma essa decisão quando o e-mail é enviado, e o endereço escolhido entra
no link. Quando alguém abre o link, o Supabase confere esse endereço de novo
com as suas configurações do jeito que estão naquele momento. A própria
resposta de solução de problemas
do Supabase resume a regra numa frase: para a opção redirectTo funcionar,
você precisa colocar a URL exata na configuração Redirect URLs.
A mesma configuração decide para onde o "Sign in with Google" devolve as pessoas, então um botão do Google que cai em localhost tem a mesma causa. Um botão do Google que para antes, numa página que diz "Unsupported provider: provider is not enabled", é outra coisa: uma chave no Supabase e uma lista no Google.
Por que você não viu isso antes do lançamento
Porque os dois jeitos com que você testou nunca chegam à Site URL.
- O login com senha acontece dentro da página. Não tem link de e-mail nem passagem pela porta do Supabase, então ninguém lê a placa.
- Se você constrói com Cursor ou Claude Code e o seu servidor de desenvolvimento estava rodando na porta 3000 quando você testou o e-mail, o link abriu a sua própria cópia local e pareceu funcionar.
- No backend integrado do Lovable talvez isso nunca aconteça com você. A
documentação de autenticação
do Lovable diz que ele coloca a Site URL no seu endereço publicado em
lovable.appquando você publica, e que adiciona um domínio próprio às redirect URLs quando você o conecta. A mesma página diz que os apps que usam o seu próprio projeto do Supabase não recebem atualizações automáticas.
A conta está confirmada mesmo a página não tendo carregado?
Sim, numa confirmação de cadastro. O Supabase confirma primeiro e só depois manda o navegador adiante.
No código que trata o link, a conta é confirmada antes de o redirecionamento sair. A página que não carrega chega com o trabalho já feito. Então diga à pessoa que ela pode entrar no seu site no ar com a senha que escolheu. Apertar o mesmo e-mail de novo não adianta: o link funciona uma vez, e o segundo aperto responde "Email link is invalid or has expired".
Um magic link ou uma redefinição de senha é diferente. Esse login foi para localhost e ficou lá, então depois que você corrigir as configurações a pessoa precisa de um e-mail novo.
Como corrigir o redirecionamento do Supabase para localhost
Troque a Site URL e depois liste os outros endereços para onde você manda as pessoas. Os dois ficam na página URL Configuration, em Authentication, no painel do Supabase.
Coloque a Site URL no seu endereço no ar, com https:// na frente.https://yourapp.com, escrito por inteiro. Sem o https://, o Supabase lê o
endereço como um caminho dentro do próprio endereço dele, e a pessoa cai no seu
projeto do Supabase com
"No API key found in request". A partir
daí qualquer endereço desse domínio é aceito em qualquer caminho sem entrada na
lista, então https://yourapp.com/welcome funciona assim que a Site URL
estiver certa.
Adicione os outros endereços em Redirect URLs. A versão com www se as
pessoas a usam, o endereço publicado no builder (https://yourapp.lovable.app,
por exemplo) se as pessoas se cadastram ali, e nada mais. Uma entrada é um
padrão, então https://yourapp.com/** cobre todas as páginas desse único
domínio.
Confira o que o seu código pede. Se ele passa window.location.origin, o
endereço é aquele em que a página foi aberta, e todo endereço que isso possa
gerar tem que estar no domínio da sua Site URL ou na lista. Compare letra por
letra: com www ou sem, com https ou sem. Uma entrada sem /** no final
bate com aquele endereço exato e com nada abaixo dele.
Confira os seus modelos de e-mail. Um modelo que monta o próprio link com
{{ .SiteURL }} manda todo mundo para a Site URL, não importa o que o seu app
pediu. O guia de redirecionamentos do Supabase diz que, ao usar a opção
redirectTo, talvez você precise substituir {{ .SiteURL }} por
{{ .RedirectTo }} nos seus modelos de e-mail.
| O que você vê | O que mudar |
|---|---|
Todo link abre localhost:3000 | A Site URL |
| Os links abrem a sua Site URL em vez do endereço que o seu código pediu | Adicione esse endereço em Redirect URLs, letra por letra |
| O domínio no ar funciona e o endereço publicado no builder não | Adicione o endereço publicado em Redirect URLs |
| Um link abre o seu endereço do Supabase com "No API key found in request" | Coloque https:// na Site URL |
| O endereço que o seu app pede está na lista e o link continua ignorando | O {{ .SiteURL }} do modelo de e-mail |
Depois cadastre-se de novo com um endereço que você ainda não usou. Um e-mail
enviado antes da correção com o link padrão do Supabase, e aberto dentro da
hora dele, agora chega ao seu site no ar, porque o Supabase confere o endereço
de novo quando o link é aberto. Isso vale a menos que http://localhost:3000
continue na sua lista.
Quais Redirect URLs é seguro permitir?
Os endereços exatos que são seus. Um curinga serve para deploys de pré-visualização, e só limitado ao seu próprio time ou projeto.
O guia de redirecionamentos do Supabase diz isso com clareza: o "globstar"
(**) é útil para desenvolvimento local e endereços de pré-visualização, mas
em produção ele recomenda definir o caminho de redirecionamento exato da sua
Site URL.
O motivo é o que viaja junto com a pessoa. No fluxo padrão, o Supabase escreve
a nova sessão no endereço para onde a manda:
o código que monta isso
acrescenta #access_token=…&refresh_token=… ao endereço de redirecionamento.
Qualquer página nesse endereço pode ler esses dois valores, e com eles ela é
aquele usuário. Na placa, é a seta que decide quem recebe a chave.
Então uma entrada que cobre um endereço controlado por outra pessoa é um caminho para dentro das contas dos seus usuários. Qualquer um pode pedir ao seu projeto que mande por e-mail um link de login para um dos seus usuários, usando a chave pública que está no seu app de propósito, e indicar a própria página como destino. O e-mail que chega é mesmo do seu app. O fluxo PKCE não muda isso, porque quem escreveu esse link começou o login, e o PKCE entrega a sessão a quem começou.
Os padrões que cobrem endereços que outras pessoas podem criar:
**sozinho cobre todos os endereços que existem.https://*.vercel.app/**cobre todos os deploys da Vercel na internet, e qualquer um pode criar um. O próprio exemplo do Supabase leva o seu time:https://*-<team-or-account-slug>.vercel.app/**.https://*.lovable.app/**,https://*.netlify.app/**e a mesma forma no domínio de qualquer builder fazem o mesmo para esse builder. O exemplo do Supabase para a Netlify também é limitado:https://**--my_org.netlify.app/**.
Se você acabou de resolver isso colando um curinga, olhe a lista de novo hoje.
O lançamento também é a hora em que as outras configurações que só importam em produção são olhadas. O nosso scan gratuito lê o seu app no ar de fora e mostra o que um estranho consegue alcançar: tabelas que respondem a qualquer um, chaves secretas no código que o seu app envia, cabeçalhos que faltam, o certificado. Leva uns 20 segundos e não precisa de conta: escaneie o seu app.
Acompanhar o app no ar depois do lançamento
Essa configuração se corrige uma vez. O que o seu app envia aos visitantes muda a cada deploy, e essa parte dá para vigiar de fora.
Reeve Monitor continua verificando o seu app no ar:
- se ele está no ar, a cada 60 segundos, para separar "o link não funciona" de "o site caiu"
- as nove verificações a cada hora, em até três apps, incluindo chaves secretas no código que o seu app envia aos visitantes
- uma mensagem quando um resultado muda, para que um problema que sai num deploy posterior chegue até você sem você precisar ir olhar
- um relatório mensal, em linguagem simples, do que ele viu
Reeve Care guarda uma cópia do seu banco de dados Supabase, contas incluídas:
- uma cópia criptografada em uma programação, no armazenamento próprio da Reeve na UE, fora da sua conta do Supabase
- o schema auth onde vivem as contas dos seus usuários, para que elas voltem com todo o resto
- cada cópia verificada antes de contar, contando as linhas de cada tabela
- seus arquivos enviados também, assim que você conectar uma credencial do Storage
- uma restauração com um clique, com uma cópia de segurança do estado atual feita antes
- tudo o que o Monitor faz
O que fazer agora
O que fazer
- Coloque a Site URL da página URL Configuration no seu endereço no ar, com
https://. - Adicione em Redirect URLs os outros endereços exatos para onde você manda as pessoas, como a versão com
wwwe o endereço publicado no builder. - Garanta que todo endereço que o seu código passa como
emailRedirectToouredirectToestá na lista, e use{{ .RedirectTo }}nos modelos que devem segui-lo. - Diga a quem viu a página de localhost depois de se cadastrar que a conta está confirmada. Magic links e redefinições de senha precisam de um e-mail novo.
- Limite os curingas às pré-visualizações do seu time, e nunca permita todos os apps do domínio de um builder.
Se as pessoas apertam o link novo e ouvem que ele expirou, essa mensagem tem uma página própria. O checklist de segurança de 10 minutos cobre o que costuma ficar aberto num app recém-lançado.
Perguntas frequentes
Por que o meu e-mail de confirmação do Supabase abre localhost:3000?
Porque o Supabase mandou a pessoa para a Site URL do seu projeto, e a Site URL ainda é http://localhost:3000. O Supabase usa ela sempre que o endereço que o seu app pediu não está na lista Redirect URLs, ou quando o seu app não pediu nenhum. Troque na página URL Configuration, em Authentication.
A conta está confirmada se o link abriu localhost?
Sim, numa confirmação de cadastro. O Supabase confirma o endereço primeiro e só depois manda o navegador adiante, então a página que falha chega com o trabalho já feito. A pessoa pode entrar no seu site no ar com a senha que escolheu. Um magic link ou uma redefinição de senha precisa ser enviado de novo.
Adicionei o meu site em Redirect URLs e continua indo para localhost. Por quê?
A entrada tem que bater letra por letra com o endereço oferecido ao Supabase: com www ou sem, com https ou sem, e o caminho, porque uma entrada sem /** no final só bate com aquele endereço exato. Se batem e o link ainda ignora, o seu modelo de e-mail monta o link com {{ .SiteURL }}. E corrija a própria Site URL, porque é para ela que todo o resto volta.
Preciso adicionar a minha Site URL na lista Redirect URLs?
Não. O Supabase aceita qualquer endereço no próprio domínio da Site URL sem entrada na lista, em qualquer caminho. A lista serve para os outros endereços: uma versão com www, o endereço publicado no builder, os deploys de pré-visualização.
Posso usar um curinga em Redirect URLs do Supabase?
Para pré-visualizações, limitado ao seu próprio time ou projeto, como nos exemplos do Supabase. Nunca um que cubra endereços que outras pessoas podem criar, como todos os apps em vercel.app ou lovable.app. O Supabase recomenda o endereço exato do seu site em produção.