Platforms and DevOps
ERR_TOO_MANY_REDIRECTS with Cloudflare Flexible SSL: fixes
ERR_TOO_MANY_REDIRECTSERR_TOO_MANY_REDIRECTS on a Cloudflare site almost always means a redirect loop between Cloudflare and your origin, most often caused by Flexible mode. Flexible mode fetches your origin over plain HTTP. An origin that redirects all HTTP to HTTPS sends the browser back to Cloudflare, which asks over HTTP again, and the cycle never ends.
What you see
Every browser reports the loop as a redirect error, not a certificate error.
| Client | Message |
|---|---|
| Chrome / Edge | "This page isn't working" and "example.com redirected you too many times." ERR_TOO_MANY_REDIRECTS |
| Firefox | "The page isn't redirecting properly" |
| curl | curl: (47) Maximum (50) redirects followed when run with -L |
Chrome also suggests deleting cookies, which rarely helps here.
How the loop works
Browser --HTTPS--> Cloudflare --HTTP--> Origin
301 Location: https://example.com/
Browser --HTTPS--> Cloudflare --HTTP--> Origin
301 Location: https://example.com/
...
The origin cannot tell that the visitor already used HTTPS, because the request it receives is HTTP. Cloudflare's documentation is explicit: if your origin forces HTTPS by redirecting HTTP requests, do not use Flexible mode.
Why so many sites end up on Flexible
Flexible exists for origins that cannot serve HTTPS at all. Many zones were set to it years ago during onboarding, and the setting outlived the reason: the origin later gained a certificate and a "force HTTPS" rule, often added by a hosting panel or a WordPress plugin, and the loop started on the day that rule was switched on. Cloudflare's current default for zones is Automatic SSL/TLS, which probes the origin and selects the most secure mode it supports, but zones with a manually chosen mode keep that choice. Check which mode is active before changing anything on the server.
Common causes, most frequent first
1. Flexible mode plus an origin HTTPS redirect
The origin has a rule such as return 301 https://$host$request_uri; in Nginx, a RewriteRule in .htaccess, a hosting panel's "force HTTPS" switch, or a framework setting that redirects insecure requests.
2. An application that thinks the request is insecure
WordPress with FORCE_SSL_ADMIN, or with its site URL set to https://, sees an HTTP request behind Flexible mode and redirects to HTTPS. Other frameworks do the same when they build absolute URLs from the request scheme.
3. Full or Full (strict) with an origin that redirects HTTPS to HTTP
This loop runs the other way. Cloudflare connects over HTTPS; an old rule on the origin sends HTTPS requests to HTTP; Cloudflare upgrades the visitor again. Cloudflare's troubleshooting page lists this for Full, Full (strict), Always Use HTTPS and HSTS.
4. www and apex redirect each other
A Cloudflare redirect rule sends www.example.com to example.com while the origin sends example.com to www.example.com. Each side is correct alone; together they loop.
Confirm the cause
A Location header that repeats in a redirect trace confirms the loop. Trace the redirects:
curl -sIL --max-redirs 6 https://example.com/ | grep -iE '^(HTTP|location|server)'
Then ask the origin directly over HTTP, bypassing Cloudflare:
curl -sI --resolve example.com:80:203.0.113.10 http://example.com/ | grep -iE '^(HTTP|location)'
A 301 to https://example.com/ from the origin, combined with Flexible mode in the Cloudflare dashboard (SSL/TLS > Overview), confirms cause 1.
How to fix it
Option A (recommended): encrypt the origin and use Full (strict). Install a certificate on the origin, a free Cloudflare Origin CA certificate or a Let's Encrypt certificate, then set the encryption mode to Full (strict). The origin's HTTPS redirect stops looping because Cloudflare now connects over HTTPS.
Option B: redirect at Cloudflare, not at the origin. Keep Flexible temporarily, remove the HTTPS redirect from the origin, and turn on Cloudflare's Always Use HTTPS so the redirect happens at the edge.
Option C: make the origin redirect depend on the visitor's scheme. Cloudflare sends X-Forwarded-Proto with the protocol the visitor used. In Nginx:
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}
In Apache:
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} =http
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
WordPress documents the same approach in wp-config.php: set $_SERVER['HTTPS'] = 'on' when HTTP_X_FORWARDED_PROTO contains https. Only trust this header if the origin accepts connections from Cloudflare alone; otherwise anyone can send it.
For cause 3, remove the HTTPS-to-HTTP rule from the origin. For cause 4, pick one canonical hostname and redirect in one place only. General redirect patterns are in redirect HTTP to HTTPS.
How to verify the fix
curl -sIL https://example.com/should end in200after at most one redirect.curl -sI http://example.com/should return a single301to the HTTPS URL.- Open the site in a private window so cached redirects do not mislead you.
- If you turned on HSTS during the change, check it with the HSTS checker; browsers then refuse HTTP for the domain, see HSTS errors.
FAQ
Chrome says "Try deleting your cookies". Will that fix it?
Not for a Flexible-mode loop. The loop is created by the server answering every request with the same redirect, so cookies play no part. Deleting cookies only helps when an application sets a cookie that triggers its own redirect logic.
Why did the site work before I enabled "force HTTPS" on the server?
Before that change the origin answered Cloudflare's HTTP request with the page itself. Once the origin redirects every HTTP request to HTTPS, and Cloudflare in Flexible mode always asks over HTTP, every answer is another redirect.
Is Flexible mode secure enough if the loop is fixed?
Visitors see a padlock, but traffic between Cloudflare and your server is unencrypted, so anyone on that path can read or alter it. Install a certificate on the origin (a free Cloudflare Origin CA certificate is enough) and use Full (strict).