# ERR_TOO_MANY_REDIRECTS with Cloudflare Flexible SSL: fixes

> Cloudflare Flexible mode plus an origin that redirects HTTP to HTTPS creates an endless redirect loop. How to confirm it and the three ways to fix it.

Source: https://ssl.sh/errors/cloudflare-flexible-ssl-too-many-redirects/
Last updated: 2026-11-10

**ERR_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

```text
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:

```bash
curl -sIL --max-redirs 6 https://example.com/ | grep -iE '^(HTTP|location|server)'
```

Then ask the origin directly over HTTP, bypassing Cloudflare:

```bash
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](https://ssl.sh/guides/cloudflare-origin-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:

```nginx
if ($http_x_forwarded_proto = "http") {
    return 301 https://$host$request_uri;
}
```

In Apache:

```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](https://ssl.sh/guides/redirect-http-to-https/).

## How to verify the fix

1. `curl -sIL https://example.com/` should end in `200` after at most one redirect.
2. `curl -sI http://example.com/` should return a single `301` to the HTTPS URL.
3. Open the site in a private window so cached redirects do not mislead you.
4. If you turned on HSTS during the change, check it with the [HSTS checker](https://ssl.sh/tools/hsts-checker/); browsers then refuse HTTP for the domain, see [HSTS errors](https://ssl.sh/errors/hsts-cannot-bypass/).
