How to Fix SSL Redirect Loop with Cloudflare Flexible SSL and Nginx Configuration


2 views

When implementing Cloudflare's Flexible SSL with Nginx, many developers encounter the dreaded redirect loop (ERR_TOO_MANY_REDIRECTS). This happens because Cloudflare acts as a proxy, creating a complex SSL termination scenario that needs proper server configuration.

With Cloudflare Flexible SSL:

User → HTTPS → Cloudflare → HTTP → Your Server

Here's the corrected configuration that handles Cloudflare's headers properly:

server {
    listen 80;
    server_name example.com www.example.com;
    
    # Only redirect if the request didn't come through Cloudflare
    if ($http_x_forwarded_proto != "https") {
        return 301 https://$server_name$request_uri;
    }
    
    # Rest of your config
    root /var/www/html;
    index index.php;
    
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
    
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }
}

server {
    listen 443 ssl;
    server_name example.com www.example.com;
    
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    
    # Tell WordPress we're using HTTPS (when behind Cloudflare)
    set $wp_https "on";
    
    location / {
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        
        try_files $uri $uri/ /index.php?$args;
    }
}

For WordPress sites, you need to modify wp-config.php:

// Cloudflare Flexible SSL fix
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 
    $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
    $_SERVER['HTTPS'] = 'on';
}

Use curl to test the headers:

curl -I http://example.com
curl -I https://example.com

Look for proper 301 redirects and no repeated Location headers.

Consider switching Cloudflare to Full SSL mode for better security:

server {
    listen 443 ssl;
    server_name example.com www.example.com;
    
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    
    # Enforce HTTPS for all connections
    if ($http_x_forwarded_proto != "https") {
        return 301 https://$server_name$request_uri;
    }
    
    # Rest of your configuration
}

When implementing Cloudflare's Flexible SSL with Nginx, a common issue occurs where the server gets stuck in an infinite redirect loop. This typically manifests as ERR_TOO_MANY_REDIRECTS in Chrome. The root cause lies in the SSL termination at Cloudflare's edge servers and how your origin server handles the requests.

With Cloudflare's Flexible SSL:

Client (HTTPS) → Cloudflare (HTTPS) → Your Server (HTTP)

Your Nginx configuration needs to properly handle this mixed protocol scenario without creating redirect loops.

The original configuration has two critical problems:

server {
    listen         80;
    server_name    example.com www.example.com;
    return         301 https://$server_name$request_uri;  # This causes the loop
}

server {
    listen 443;
    server_name example.com www.example.com;
    # Missing proper SSL configuration
}

Here's the corrected Nginx configuration:

server {
    listen 80;
    server_name example.com www.example.com;
    
    # Cloudflare IP ranges
    set_real_ip_from 103.21.244.0/22;
    set_real_ip_from 103.22.200.0/22;
    # ... (all Cloudflare IP ranges)
    real_ip_header CF-Connecting-IP;
    
    # Only redirect to HTTPS if request didn't come through Cloudflare
    if ($http_cf_visitor) {
        set $ssl_redirect off;
    }
    if ($http_x_forwarded_proto = "https") {
        set $ssl_redirect off;
    }
    
    if ($ssl_redirect = on) {
        return 301 https://$host$request_uri;
    }
    
    # WordPress rewrite rules
    location / {
        try_files $uri $uri/ /index.php?$args;
    }
    
    # Additional WordPress PHP handling
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass php-fpm;
        # ... (other PHP config)
    }
}

1. Cloudflare IP Handling: Properly sets real client IPs through Cloudflare's proxy

2. SSL Redirect Logic: Only redirects to HTTPS if not coming through Cloudflare

3. Protocol Detection: Checks both CF-Visitor and X-Forwarded-Proto headers

Add these to wp-config.php:

if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
    $_SERVER['HTTPS'] = 'on';
}

This ensures WordPress generates correct URLs when behind Cloudflare's proxy.

Use curl to verify:

curl -I -H "Host: example.com" http://your-server-ip/

Should show a single redirect (if not behind Cloudflare) or no redirect (if behind Cloudflare).