Troubleshooting an endless redirect loop for a site behind Cloudflare

What can you do when you don't know (or have forgotten) that a setting exists?

I spent a few very frustrating hours trying to figure out why HTTPS requests to for a particular domain (behind Cloudflare) kept providing redirects to the same URI. I checked and re-checked my web server configuration, deleting lines, re-adding lines, and refreshing my browser. It turned out that I needed to change the encryption mode in Cloudflare!

The web server configuration #

I configured the web server to:

server {
    server_name example.com *.example.com;
    listen 80;
    listen [::]:80;

    if ($host ~ (^[^.]+\.example\.com$|^example.com$)) {
        return 301 https://$host$request_uri;
    }

    # Should never get here!
    return 404;
}

server {
    server_name example.com *.example.com;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    autoindex off;

    return 307 https://www.example.com;
}

server {
    server_name www.example.com;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    root /var/www/example.com;
}

Troubleshooting the problem #

I expected the following:

Request Behaviour
http://example.com/ Redirect to https://example.com/
http://www.example.com/ Redirect to https://www.example.com/
https://example.com/ Redirect to https://www.example.com/
https://www.example.com/ Serve site content

HTTP requests redirected to HTTPS, as expected.

However, HTTPS requests resulted in an endless loop to the same URI!

I spent entirely too much time testing and re-testing the web server configuration before I decided to try making requests directly to the web server itself, as requests to the domain itself are handled by a Cloudflare proxy.

I learned that I could inspect an HTTPS response to the web server's IP address via curl using three arguemnts:

So I reviewed the response I got in my browser from Cloudflare and compared it to the response I got making a curl request to the web server's IP address:

curl -ik --header 'Host: example.com' https://12.34.56.78

The server presented the behaviour I expected to curl, but Cloudflare presented the behaviour for an HTTP request to my browser!

Fixing the Cloudflare setting #

From there, I quickly found out (or maybe more accurately re-discovered) that Cloudflare's encryption mode for a site controls how Cloudflare connects to the origin server [4]. The encryption mode was set to Flexible, which meant that Cloudflare accessed the site via HTTP and presented the response to the requester via HTTPS. I changed the encryption mode to Full (strict) to have Cloudflare only use HTTPS.

Screenshot of Cloudflare encryption mode

If I'd only searched for the right terms, I might have seen Cloudflare's troubleshooting page on ERR_TOO_MANY_REDIRECTS [5], which addresses this exact scenario. Well, at least now I know how to make requests for a particular host and skip certificate validation using curl.

Footnotes #

[1] Telling curl to include HTTP response headers in the output (curl.se) ^

[2] Telling curl to skip certificate verification (curl.se) ^

[3] Manually setting the Host header for a curl request (stackoverflow.com) ^

[4] Cloudflare Docs: Encryption modes (developers.cloudflare.com) ^

[5] Cloudflare Docs: ERR_TOO_MANY_REDIRECTS (developers.cloudflare.com) ^