ERR_TOO_MANY_REDIRECTS: The Five Causes, in Order

A redirect loop means two rules are each certain they are right. Here is how to find which two, and the five configurations behind almost every case.

喜欢就分享一下吧

A browser will follow about twenty redirects before it stops and shows ERR_TOO_MANY_REDIRECTS. Firefox words it as "The page isn't redirecting properly" and Chrome sometimes says "This webpage has a redirect loop". They all mean the same thing.

The error is never the whole story, because the loop is almost always two rules that each think they are correct, made in different places by different people at different times. Neither is wrong on its own. Together they undo each other forever.

First, see the loop

Diagnosing this in a browser is impossible, because the browser follows every hop before it paints anything and then shows you an error page instead of the chain. You need the hops.

By hand, that means requesting the URL, reading the Location header off the response, requesting that, and repeating. One r/nginx commenter described the method exactly: call the URL, take the location header, call it again, "you will probably get the same URI again in the second redirect, that is your loop."

The Redirect Checker does that loop for you and stops as soon as an address repeats, rather than following it twenty times. What you are looking for is the same URL appearing twice. The two hops between those appearances are produced by the two rules that are fighting.

A loop almost always looks like one of these:

https://site.com/  →  https://www.site.com/  →  https://site.com/   (host rule vs. host rule)
http://site.com/   →  https://site.com/      →  http://site.com/    (protocol rule vs. origin)

Once you know which pair of addresses is alternating, the cause is usually obvious from the list below.

1. Cloudflare SSL/TLS set to Flexible

This is the most common cause by a wide margin, and the signature is unmistakable: the site worked fine until it was put behind Cloudflare, and it still works if you hit the origin directly.

On Flexible mode, Cloudflare requests your origin over plain HTTP. Your origin, quite reasonably, redirects HTTP to HTTPS. Cloudflare passes that redirect back to the browser. The browser asks Cloudflare again over HTTPS, and Cloudflare goes back to the origin over HTTP. Nothing in that cycle ever terminates.

Fix. In the Cloudflare dashboard, go to SSL/TLS and change the encryption mode from Flexible to Full, or Full (Strict) if your origin has a valid certificate. Cloudflare then speaks HTTPS to the origin, the origin's redirect never fires, and the loop stops immediately.

This fix comes up again and again in self-hosting threads, on Jellyfin, Nextcloud and Unifi setups, usually as a relieved reply months after the original question.

2. WordPress home and siteurl disagreeing with the server

The second signature: a WordPress site that loops on some requests, or loops on the front end while the admin still works, or started looping after a domain or host change.

WordPress stores its own idea of where the site lives in the home and siteurl options. If those say https://site.com while your server, host panel or CDN rewrites everything to https://www.site.com, the two rewrite each other on every request.

Fix. Make them agree. Decide which host is canonical, then set both options to it. If the loop has locked you out of the admin screens, you have two routes in:

  • Define them in wp-config.php, which overrides the database:
define('WP_HOME',    'https://www.example.com');
define('WP_SITEURL', 'https://www.example.com');
  • Or edit the home and siteurl rows in the wp_options table directly.

Then re-save your permalinks so .htaccess is regenerated. A redirect plugin can also cause this on its own: some create a redirect automatically whenever a post slug changes, and renaming a post back to its old slug leaves a rule pointing at itself.

3. A reverse proxy that drops the Host header

The signature here is that the backend is reachable directly on IP:PORT but loops through the proxy.

The backend receives a request whose Host is an IP address, decides that is not the hostname it is configured for, and issues a redirect to its proper name. That redirect goes back through the proxy, arrives with the IP again, and the backend redirects again.

Fix. Forward the real host on the proxied request:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;

X-Forwarded-Proto matters as much as Host. Without it, an application behind a TLS-terminating proxy sees a plain HTTP request and tries to upgrade a connection that is already secure.

4. TLS terminated twice

Closely related, and common when someone adds a proxy in front of a server that was previously public.

If the proxy already terminates TLS and the origin still carries its own HTTPS-forcing server block, the origin keeps trying to upgrade a connection the proxy has already handled.

Fix. Remove the redundant HTTPS redirect from the origin. It should serve plain HTTP on the internal network and let the proxy own the certificate and the upgrade. One rule, one place.

5. www and non-www with no rule at all

This one produces no error, which is why it survives for years.

Both hostnames serve the same pages, neither redirects to the other, and no canonical tag ties them together. Nothing breaks. The site is simply available at two addresses, and search engines have to guess which one is real.

A study of 2,165 small business sites posted to r/TechSEO in 2026 found about one in twenty doing exactly this, with roughly a third of those concentrated on a single hosting provider. As one commenter put it, the usual path is a host migration where nobody checked what the canonical was declared as afterwards.

Fix. Pick one host. Redirect the other to it with a single 301. Confirm with a trace that the redirect is one hop and the survivor returns 200 with a canonical pointing at itself.

Check the fix, do not assume it

Two things are worth confirming after any of these changes.

That the loop is gone for a crawler, not just for you. A browser caches a permanent redirect aggressively, and a cached 301 will keep looping long after the server stops sending one. Test in a private window, or trace the URL with a tool that makes a fresh request every time.

That you did not trade a loop for a chain. The fastest fix for a loop is often to add one more rule, which stops the error and leaves three hops where there should be one. Trace it again afterwards and confirm the path is a single redirect to a 200.

If you want the background on which code to use once the loop is fixed, the 301 vs 302 guide covers what each one tells a search engine and how chains accumulate.