
A descrição do próprio Chromium é admiralvemente simples: “A tentativa de carregar uma URL resultou em muitos redirecionamentos.” O navegador seguiu uma cadeia de redirecionamentos, a cadeia não terminou e em algum ponto parou de seguir.
O número em que para vem do Fetch Standard, que é inequívoco sobre isso: se o redirect count da requisição é 20, então retorna um erro de rede. Portanto, vinte saltos, e depois a página que você vê é do navegador, não sua.
O que torna esse erro digno de seu próprio artigo não é o mecanismo, que é óbvio. É que um loop de redirecionamento quase nunca é uma única coisa quebrada. São duas coisas, cada uma se comportando exatamente como configurado, discordando sobre algo que nenhuma delas consegue ver.
O clássico, que é uma discordância sobre HTTPS
Você coloca um CDN ou um balanceador de carga na frente de seu aplicativo. Ele encerra TLS, que é seu trabalho, e encaminha a requisição para seu origin por HTTP simples, que é normal e geralmente deliberado.
Seu aplicativo tem uma regra que diz: se essa requisição não for HTTPS, redirecione-a para HTTPS. Também correto, e em um servidor sem proxy na frente, exatamente certo.
Agora junte-os. O navegador pede https://example.com. O CDN encerra TLS e encaminha
http://example.com para seu app. Seu app vê HTTP simples, faz a coisa sensata e responde
“redirecione para https://example.com”. O navegador obedece. O CDN encerra TLS e encaminha
HTTP. Vinte vezes, e depois o navegador desiste.
Nenhum componente está mal configurado por si só. O aplicativo está forçando HTTPS. O proxy está encerrando TLS. O bug vive na lacuna entre eles, por isso ambas as equipes podem olhar para sua própria configuração e concluir corretamente que está bem.
A solução é que o app tem que parar de fazer a pergunta errada. Em vez de “essa requisição
foi HTTPS”, ele precisa perguntar “a requisição original foi HTTPS”, que é para o que o
cabeçalho X-Forwarded-Proto existe. A maioria dos frameworks tem uma configuração para
confiar nesse cabeçalho de um proxy conhecido, e geralmente está desativada por padrão por
uma boa razão: um cabeçalho que qualquer um pode definir não é evidência a menos que você
saiba quem o definiu.
A mesma forma, três vezes mais
Uma vez que você vê o padrão, os outros loops comuns têm a mesma história com nomes diferentes.
www contra o apex. Uma camada redireciona example.com para www.example.com. Outra,
adicionada depois ou por outra pessoa, redireciona www de volta ao apex. Cada regra é
defensável. O par é um loop.
A barra à direita. Seu servidor web redireciona /about para /about/. O roteador do
seu framework redireciona /about/ para /about. Ambos estão forçando uma forma canônica.
Discordam sobre qual é a canônica.
O login loop, que é o que chega aos usuários reais. O app não vê uma sessão válida, então
redireciona para /login. A página de login verifica o cookie de sessão, acredita que está
conectado e redireciona para o app. Volta e volta.
Vale a pena refletir sobre esse último, porque a causa geralmente é o cookie não persistir
em vez das regras de redirecionamento estarem erradas: um cookie Secure em uma página
servida por HTTP, um cookie SameSite=None sem Secure, um cookie definido em
www.example.com sendo lido em example.com, ou um navegador configurado para recusar
cookies de terceiros em um contexto incorporado. A lógica de redirecionamento está correta
por toda parte. Ela está agindo sobre uma sessão que nunca persistiu.
- Fraco
- "Sua página de login está presa em um loop. Ela só fica recarregando."
- Melhor
- ERR_TOO_MANY_REDIRECTS em /login, apenas em uma janela privada, apenas depois que faço logout e login novamente. Funciona minha janela normal. Cadeia é /app → /login → /app.
Por que esse é inusualmente difícil de ver de dentro
Um loop de redirecionamento é geralmente específico para um estado em que você não está.
Se for o login loop, seu cookie já está definido e funcionando, então o site se comporta perfeitamente para você e faz loop para qualquer um que chegue novo. Se for uma regra www que alguém adicionou semana passada, faz loop apenas para pessoas que digitaram o outro nome de domínio. Se depender de um cookie que o navegador está recusando, reproduz em uma janela privada e em nenhum outro lugar.
E a verificação óbvia erra. curl -I https://example.com mostra uma resposta, que será um
301 perfeitamente razoável com um cabeçalho Location. Nada sobre isso diz que a cadeia
nunca termina. O que você quer é seguir o todo:
curl -sIL -o /dev/null -w '%{url_effective} %{http_code}\n' https://example.com
curl -sI https://example.com | grep -i '^location'
Ou abra a guia Rede com
Preservar registro marcado, que é a única maneira de manter os saltos anteriores em vez
de limpá-los a cada navegação. A cadeia é o diagnóstico: leia o cabeçalho Location a
cada passo e você geralmente pode ver as duas regras argumentando dentro de três saltos.
Session Replay
Extensão gratuita do Chrome. Um clique na página que está se comportando mal captura a screenshot, o console e o log de rede, e oferece a você um link para colar no ticket.
Uma coisa sobre 301 que vale a pena saber antes de usá-lo
Um 301 não é simplesmente um 302 que significa mais.
O MDN é específico sobre uma consequência que surpreende as pessoas: no Fetch Standard, um
user agent recebendo um 301 em resposta a um POST usa GET para a requisição
redirecionada. Seu envio de formulário se torna um GET, o corpo desaparece e a falha não se
parece nada com um problema de redirecionamento. Se você precisar que o método seja
preservado, 308 é o que proíbe mudá-lo.
A regra geral é mais chata e mais útil: use 302 ou 307 enquanto ainda está descobrindo
qual é a URL canônica, e use 301 ou 308 quando tiver certeza. Um redirecionamento
permanente é uma afirmação, e afirmações são muito mais fáceis de fazer do que retirar.
O que levar disso
ERR_TOO_MANY_REDIRECTS significa vinte saltos sem um fim, e o fim está faltando porque
duas regras discordam, não porque uma está quebrada.
Então não comece procurando pela regra quebrada. Comece listando cada camada que tem permissão
para emitir um redirecionamento: o CDN, o balanceador de carga, o servidor web, o framework,
o aplicativo e tudo o que um plugin adicionou sem avisar a ninguém. Depois leia a cadeia, um
cabeçalho Location de cada vez, e encontre os dois que estão argumentando.
E quando não se reproduz para você, isso é informação em vez de um obstáculo. Significa que o loop depende de um estado que você já passou, e a forma mais rápida para ele é uma janela privada, um nome de domínio diferente, ou a visão completa do que o navegador realmente recebeu de alguém para quem está falhando agora.