Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild
Back to Blog Troubleshooting

ERR_TOO_MANY_REDIRECTS: Finding the Loop and Breaking It

Sean

Platform Writer

Aug 13, 2026
8 min read

A redirect loop means two things are each redirecting to the other. The overwhelmingly common cause is a proxy or CDN terminating TLS and forwarding to your origin over plain HTTP, while the origin redirects all HTTP to HTTPS — so the proxy sends HTTP, the origin says “go to HTTPS”, the proxy answers that on HTTPS and forwards over HTTP again, forever. The fix is to make the origin trust X-Forwarded-Proto rather than guess from the connection it can see.

ERR_TOO_MANY_REDIRECTS: Finding the Loop and Breaking It

Every browser caps redirects at around 20 and then gives up with this error. Finding the loop takes one command; the fix depends on which layer is guessing.

Table of contents

See the chain first

# Follow redirects and print every hop
curl -IL --max-redirs 10 https://example.com/

# Just the codes and destinations
curl -sIL --max-redirs 10 https://example.com/ \
  | grep -Ei '^(HTTP/|location:)'

The output shows the cycle plainly:

HTTP/2 301
location: https://www.example.com/
HTTP/2 301
location: https://example.com/
HTTP/2 301
location: https://www.example.com/

That is a www loop: one rule adds www, another removes it. Now you know which two rules to look at, which is most of the work.

Check both entry points, because a loop often exists on only one:

curl -sIL http://example.com/  | grep -Ei '^(HTTP/|location:)'
curl -sIL https://example.com/ | grep -Ei '^(HTTP/|location:)'
curl -sIL https://www.example.com/ | grep -Ei '^(HTTP/|location:)'

The HTTPS loop, which is most of them

A CDN or load balancer accepts HTTPS and forwards to your origin over HTTP. The origin sees an HTTP request and redirects to HTTPS. The proxy serves that redirect, the browser comes back on HTTPS, the proxy forwards over HTTP again, and round it goes.

The origin is not wrong to want HTTPS; it is wrong about how it detects it. It must read the forwarded header rather than the connection scheme.

# nginx: redirect only when the ORIGINAL request was HTTP
server {
    listen 80;
    server_name example.com;

    if ($http_x_forwarded_proto = "http") {
        return 301 https://$host$request_uri;
    }
}
// Express behind a proxy
app.set('trust proxy', 1);

app.use((req, res, next) => {
  if (req.secure) return next();          // now reads X-Forwarded-Proto
  res.redirect(301, `https://${req.headers.host}${req.originalUrl}`);
});

Most frameworks have this switch: trust proxy in Express, SECURE_PROXY_SSL_HEADER in Django, ProxyFix in Flask, config.force_ssl with trusted proxies in Rails. Setting it is the actual fix; disabling the redirect is only a workaround.

Only trust the header when a proxy you control is definitely in front. Trusting X-Forwarded-Proto on a directly exposed server lets any client claim its request was secure.

The CDN encryption-mode loop

A specific and very common variant. When a CDN is set to Flexible encryption, it accepts HTTPS from the browser and always connects to your origin over HTTP — so an origin that redirects HTTP to HTTPS loops immediately.

  • Flexible — CDN to origin is always plain HTTP. Loops against any origin that forces HTTPS.
  • Full — CDN to origin over HTTPS, certificate not validated. No loop.
  • Full (strict) — CDN to origin over HTTPS with a valid certificate. No loop, and the correct setting.

The fix is to install a certificate on the origin and move to Full (strict), not to remove the origin’s redirect. Flexible means the second half of every request travels unencrypted, which defeats most of the point.

HSTS interacts badly here too: once a browser has the header cached it upgrades to HTTPS itself, which can create a loop that persists after you fix the server. Test in a private window or clear the domain’s HSTS entry while debugging.

The www loop and the trailing-slash loop

Two rules, each authoritative, disagreeing. Pick one canonical hostname and make everything else redirect to it, once.

# nginx: one canonical host, one redirect
server {
    listen 443 ssl;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;
    # the real site
}

Check for a competing rule in the application. A CMS with a configured site URL will enforce its own preference, and a mismatch between that and the web server config is a reliable loop.

Trailing slashes cause the same shape: the server appends a slash for directories while the application strips it. Decide which form is canonical and configure both layers to agree.

WordPress and other CMS-specific loops

WordPress stores its own site URL in the database and redirects to it. If siteurl says http:// while the site is served over HTTPS behind a proxy, you get an immediate loop that surviving configuration changes will not fix.

// wp-config.php -- above the "stop editing" line
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
    && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

define('WP_HOME',    'https://example.com');
define('WP_SITEURL', 'https://example.com');

Defining the constants overrides the database values, which is the reliable way to break a loop you cannot log in to fix — because the admin login is behind the same redirect.

A caching plugin that cached the redirect will keep serving it after the fix. Clear the cache and check with a fresh curl -IL rather than a browser reload.

A checklist, in the order that finds it fastest

  1. curl -sIL the URL and read the Location chain. Identify the two hosts or schemes swapping.
  2. Test http and https, with and without www, separately. The loop is often on one entry point only.
  3. If a CDN is in front, check its encryption mode before touching anything on the origin.
  4. Check whether the origin redirects based on the connection scheme instead of X-Forwarded-Proto, and enable the framework’s proxy trust setting.
  5. Look for a second redirect rule in the application or CMS that competes with the web server.
  6. Clear caches — CDN, application, and browser HSTS — and re-test with curl, not a browser.

The pattern behind nearly all of these is the same: a layer deciding something it cannot actually observe. The origin cannot see the browser’s scheme, only what the proxy sent it. Once it reads the header instead of guessing, the loop cannot form.

This class of problem is also much less likely when TLS termination, the certificate, and the redirect are one configured thing rather than three that have to agree. Attaching a custom domain on RunxBuild handles the certificate and the HTTPS path together, and redirects are declared as configuration rather than assembled from rules in two layers.

How this fits the rest of the stack

Run curl -sIL and read the Location chain — it names the two rules fighting. Most loops are an origin forcing HTTPS while a proxy forwards HTTP, so enable your framework’s proxy trust setting and check the CDN is not on Flexible encryption. For www and trailing slashes, pick one canonical form and make every other layer agree.

Fewer layers guessing means fewer loops. If you are working out what a setup with managed certificates and declared redirects costs, the RunxBuild hosting calculator shows the service, database, storage, and bandwidth as separate line items.

Useful related references:

FAQ

What causes ERR_TOO_MANY_REDIRECTS?

Two rules redirecting to each other. The most common pairing is a proxy or CDN that terminates TLS and forwards to the origin over plain HTTP, while the origin redirects all HTTP traffic to HTTPS. Each layer is behaving correctly in isolation and together they form a cycle the browser gives up on.

How do I find where a redirect loop is coming from?

Run curl -sIL --max-redirs 10 https://example.com/ and read the Location headers. The chain shows exactly which two hosts or schemes are alternating, which identifies the two rules to inspect. Test http and https, with and without www, since a loop often exists on only one entry point.

Why does my site loop when I enable HTTPS on a CDN?

The CDN is probably set to Flexible encryption, which always connects to your origin over plain HTTP. Your origin sees HTTP, redirects to HTTPS, and the cycle begins. Install a certificate on the origin and switch to Full (strict) rather than removing the origin’s redirect.

How do I fix a WordPress redirect loop?

Define WP_HOME and WP_SITEURL in wp-config.php to override the database values, and set $_SERVER['HTTPS'] = 'on' when HTTP_X_FORWARDED_PROTO is https. This works even when you cannot reach the admin panel, since the login page is behind the same loop. Clear any caching plugin afterwards.

Why does the loop persist after I fixed the server?

Something cached the redirect. Browsers cache 301s aggressively, HSTS makes the browser upgrade to HTTPS on its own, and CDN or plugin caches may still hold the old response. Verify the fix with curl -IL rather than a browser reload, and test in a private window.

#redirect loop#err_too_many_redirects#https#nginx#cloudflare