Redirect Loop: Why ERR_TOO_MANY_REDIRECTS Happens and How to Fix It
A redirect loop is a chain that revisits a URL it has already been through, so no request ever reaches content. Browsers detect the cycle after a bounded number of hops and give up with an error such as Chrome's ERR_TOO_MANY_REDIRECTS.
Why it matters
Redirect loops are almost always two rules disagreeing. The CDN forces https while the origin (seeing terminated TLS) forces http back. The CMS adds a trailing slash while a rewrite strips it. A geo rule bounces between variants. The page is fully down for users and crawlers, so diagnosis speed matters. The diagnostic is simply seeing the cycle, which is exactly what a recorded hop list gives you.
A loop is a critical incident: users cannot access the site, crawlers see the page as broken, and search results go stale. Speed of diagnosis determines recovery time. Most loops are five-minute fixes once you see them.
How it works
The browser requests a URL, gets a 3xx with a Location header, follows it to the next URL, and that URL sends a 3xx back to the first. After bouncing between the same two (or more) URLs, the browser detects the cycle. Chrome stops at 20 internal redirects and shows ERR_TOO_MANY_REDIRECTS; other browsers have similar limits.
The cycle is always a configuration mistake: two rules that should cooperate work against each other. A classic example: the CDN strips the port, the origin rewrites it back based on a rule that sees no port and assumes insecure, the CDN enforces secure again, and the two trade redirects forever.
What does not matter
Loops are not a sign of a fundamental design flaw. They are configuration contradictions, and configuration contradictions are solved by reading what each layer sees and fixing the logic, not by redesigning the architecture. Cookie-dependent or state-dependent loops (a rule fires if a flag is set) require live observation to reproduce, but the diagnosis method is the same.
Code example
Two rules that loop:
# CDN or proxy layer
server {
if ($scheme = http) {
return 301 https://$server_name$request_uri;
}
}
# Origin layer (sees X-Forwarded-Proto because TLS terminates at CDN)
server {
if ($http_x_forwarded_proto = http) {
return 301 https://$server_name$request_uri;
}
}
The problem: the origin receives X-Forwarded-Proto: http (because the CDN still reports the client's original scheme), sees http, and redirects to https. The CDN receives the https request but rewrites it back to http in its own check. Loop.
The fix: trust the proxy's header at the origin.
server {
# Detect the real scheme from the proxy, not the direct connection
if ($http_x_forwarded_proto = http) {
return 301 https://$server_name$request_uri;
}
}
With this change, the origin trusts the CDN's report of the client's intent and does not fight back.
How Scalpel shows it
A chain that keeps 3xx-ing gets the "Redirect loop suspected" verdict and an amber 3xx badge. The hop list shows the repeating URLs, capped at 30 hops with a truncation flag. The hop sequence makes the problem visible: you see URL A → B → A → B and can immediately trace each to its source.