ERR_TOO_MANY_REDIRECTS (tested)
ERR_TOO_MANY_REDIRECTS means the browser followed redirects until it hit its limit and gave up. The page is not slow. Two URLs are sending you to each other. We reproduced the common causes and counted every hop on the server.
1. What the error means
A redirect is a 3xx response with a Location header. The browser follows it. If URL A redirects to B and B redirects back to A, the chain never ends. Every client has a limit. When it is reached, Chromium shows ERR_TOO_MANY_REDIRECTS. Firefox calls it "The page isn't redirecting properly".
We ran a Node server where /a returned 302 to /b and /b returned 302 to /a. The server counted every request.
2. How many hops each client follows
| Client | Error it gave | Requests the server saw |
|---|---|---|
| Chromium 153 | net::ERR_TOO_MANY_REDIRECTS | 20 (10 to /a, 10 to /b) |
| curl -L (default) | curl: (47) Maximum (50) redirects followed, exit code 47 | 51 |
| curl -L --max-redirs 5 | Maximum (5) redirects followed | 6 |
| Node.js 24 fetch | fetch failed, cause redirect count exceeded | 21 |
| Python urllib | HTTPError 302 "redirect error that would lead to an infinite loop" | 9 |
Python stops early because urllib also refuses to visit the same URL more than 4 extra times. It saw /a 5 times and gave up. The others count hops only.
To see the loop yourself, print each hop without following too far:
curl -sIL --max-redirs 6 https://example.com/ | grep -iE '^(HTTP|location)'
If the same two location values alternate, you have a loop. Our redirect chain guide covers this in detail.
3. The classic cause: HTTPS redirect behind a proxy
A load balancer or CDN terminates TLS. It then talks plain HTTP to your app. The app has a rule "if the request is not HTTPS, redirect to HTTPS". The app only sees HTTP, so it redirects. The browser comes back over HTTPS, the proxy forwards it as HTTP again, and the loop never ends.
We built this: a small Node proxy on one port forwarding to an app on another. The app redirected with 301 whenever it thought the request was not HTTPS.
| Setup | Result |
|---|---|
Proxy sends no X-Forwarded-Proto | Loop. curl --max-redirs 10 failed with error 47. The app saw 11 requests. |
Proxy adds X-Forwarded-Proto: https, app reads it | 200 on the first request. 0 redirects. |
| Proxy adds the header, app ignores it | Still a loop. 11 requests, error 47. |
So the fix needs both halves. The proxy must send the header. The app must trust it. In Express that is:
app.set('trust proxy', 1); // trust the first proxy hop
app.use((req, res, next) => {
if (req.secure) return next(); // req.secure uses X-Forwarded-Proto now
res.redirect(301, 'https://' + req.headers.host + req.url);
});
In nginx, as the proxy:
proxy_set_header X-Forwarded-Proto $scheme;
Only trust these headers from your own proxy. See X-Forwarded-For and the real client IP and Forwarded vs X-Forwarded-For.
4. Cloudflare Flexible SSL and WordPress (from the docs, not measured)
Cloudflare's "Flexible" SSL mode connects to your origin over HTTP. If the origin forces HTTPS, it redirects every request, and Cloudflare's documentation lists this as a cause of redirect loops. The documented fix is to install a certificate on the origin and set SSL mode to "Full" or "Full (strict)".
In WordPress, the siteurl and home settings must match the URL visitors use. If one says http:// and the site forces HTTPS, or one has www and the server strips it, WordPress and the server can redirect against each other. Check both values in Settings, General, or in wp-config.php with WP_HOME and WP_SITEURL.
5. Cookie loops: login that never sticks
/login sets a session cookie and redirects to /app. /app does not see the cookie and redirects back to /login. We tested three cookies in Chromium on http://127.0.0.1:
| Cookie set by /login | Chromium result |
|---|---|
Path=/ | 200 at /app. 1 request each. |
Path=/path/login (too narrow) | ERR_TOO_MANY_REDIRECTS. 10 requests to each URL. The cookie was stored but never sent to /app. |
Path=/; Secure over plain http | 200 at /app. Chromium stored and sent the Secure cookie. |
The Secure result surprised us at first. Chromium treats 127.0.0.1 and localhost as secure contexts, so it accepted the Secure cookie over HTTP. On a real http:// domain a Secure cookie is rejected, and the same code would loop. Test on a real hostname before you trust a local result. Wrong Domain or SameSite settings fail the same way. See SameSite Lax, Strict and None.
6. Fixed it, still looping? The cached 301
Browsers cache 301 redirects. We made /p and /q loop with 301, loaded it in Chromium, then fixed the server so /q returned 200.
| Step | Result | Requests to server |
|---|---|---|
| First visit, loop | ERR_TOO_MANY_REDIRECTS | 2 (one each, the rest came from cache) |
| Server fixed, same browser profile | Still ERR_TOO_MANY_REDIRECTS, also for /q directly | 0 |
| Server fixed, fresh profile | 200 "q ok" | 2 |
The fixed server never received a request from the old profile. Clear the site's cache and cookies, or test in a private window. While you debug, use 302 or 307 so nothing gets cached. See 301 vs 302 vs 307 vs 308.
7. Checklist
1. Print the chain with curl -sIL --max-redirs 6 and find the two URLs that alternate. 2. If it flips between http and https, check your proxy headers or CDN SSL mode. 3. If it flips between www and non-www, check every layer that rewrites the host. 4. If it flips between /login and a protected page, check the cookie's Path, Domain, Secure and SameSite. 5. After the fix, test in a fresh profile because of cached 301s.
Frequently asked questions
How many redirects does Chrome follow before ERR_TOO_MANY_REDIRECTS?
In our test Chromium 153 made 20 requests on a two-URL loop before it gave up with net::ERR_TOO_MANY_REDIRECTS.
Why does my site loop only behind Cloudflare or a load balancer?
The proxy speaks HTTP to your app, so an HTTPS-forcing rule fires on every request. Send X-Forwarded-Proto: https from the proxy and make the app trust it, or use end-to-end TLS. In our test, the header alone was not enough. The app also had to read it.
I fixed the server but the browser still loops. Why?
Browsers cache 301s. In our test Chromium kept failing with zero requests reaching the fixed server until we used a fresh profile. Clear the site's cache or use a private window.
Can a cookie cause ERR_TOO_MANY_REDIRECTS?
Yes. If the login cookie is never sent back (wrong Path, Domain, Secure on HTTP, or SameSite), the protected page keeps redirecting to login. A Path set too narrowly reproduced the loop in our test.
What does curl error 47 mean?
curl followed its redirect limit and stopped. The default with -L is 50, shown as 'Maximum (50) redirects followed'. Use --max-redirs to set a lower limit while debugging.
Written with AI assistance. Measured on 2026-10-02 with headless Chromium 153 (Playwright 1.63), curl 8.14.1, Node.js 24.21.0 fetch and Python 3.13.5 urllib, against small Node servers on 127.0.0.1 that counted every request. PlainHeaders is our extension; curl, Python, Node.js and Chromium are free.