What each client says
| Client | Error | Gives up after |
|---|---|---|
| Chrome, Edge | ERR_TOO_MANY_REDIRECTS, "redirected you too many times" | 20 |
| Firefox | "The page isn't redirecting properly" | 20 |
| Safari | "Too many redirects occurred trying to open" | About 16 |
curl -L | Maximum (50) redirects followed | 50, or --max-redirs |
See the loop
Put the URL into the HTTP header and redirect checker. It follows each redirect itself, shows every hop's status and Location, and stops with a "redirect loop" warning when a URL comes round a second time. From a shell:
curl -sIL --max-redirs 10 http://example.com/ | grep -iE '^(HTTP|location)'
The pattern in the hops names the cause. Look for the two URLs that alternate.
https to https to https: a proxy and the app disagree about the scheme
Every hop is the same https:// URL, redirecting to itself. This is the most common loop I know of, and it always has the same shape: TLS ends at something in front of the application (a CDN, a load balancer, a reverse proxy), which then talks to the application over plain HTTP. The application sees an HTTP request, and its "force HTTPS" setting redirects to https://. The browser goes back to the proxy over HTTPS, the proxy talks HTTP to the app again, and round it goes.
- Cloudflare "Flexible" SSL is the textbook case: Cloudflare to origin over HTTP, origin redirecting to HTTPS. Set the SSL/TLS mode to Full (strict) and give the origin a certificate (Cloudflare's origin certificates are free).
- Behind a load balancer, tell the application to trust the proxy's
X-Forwarded-Protoheader:SECURE_PROXY_SSL_HEADERin Django,config.assume_sslwithforce_sslin Rails,app.set('trust proxy', ...)in Express, trusted proxies in Laravel,$_SERVER['HTTPS']set from the header for WordPress. In nginx in front of it,proxy_set_header X-Forwarded-Proto $scheme;.
example.com to www to example.com: two layers want different hosts
The hops alternate between apex and www. One layer (the registrar's forwarding, the CDN's page rules, the web server) sends the apex to www; another (the application's site URL setting, a WordPress siteurl, a framework's canonical host) sends www to the apex. Pick one canonical host, make exactly one layer enforce it, and remove the rule from the other.
/path to /path/ to /path: slash rules
A framework adds a trailing slash to directory-like paths and a proxy or CDN rule strips it, or the other way round. Same fix: decide which form is canonical and have one layer enforce it.
Only in a browser, or only when logged in: cookies
If the checker follows the URL to a normal page but your browser loops, suspect cookies. The checker keeps none, so it sees what a first-time visitor sees. A browser with a stale session cookie, or one that cannot store a new one, can loop between a login page and the page that requires login:
- The session cookie is set for the wrong domain (
example.comversuswww.example.com) or path, so it is never sent back. - It is marked
Securebut the app thinks it is on HTTP (the proxy problem above), orSameSite=Stricton a flow that arrives from another site, such as an SSO login. - An old cookie from before a deploy that the new code rejects and redirects on, without clearing it.
Clearing the site's cookies confirms it in seconds. The fix is on the server: set the cookie for the right domain, and clear invalid sessions instead of redirecting on them.
Fixed on the server, still looping in your browser
Browsers cache 301 and 308 redirects, sometimes for a long time. If the checker now shows a clean chain and your browser still loops, it is replaying a cached redirect. Test in a private window or another browser; visitors who hit the loop will recover when the cache expires, or sooner if they clear it. This is the reason to use temporary redirects while you are still changing things, as 301 vs 302 vs 307 vs 308 explains.
Once the chain is clean, collapse it: http://example.com should reach the final page in one hop, not three. The HTTP checker counts the hops, and Domain Health flags chains that end on an unfollowed redirect.