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).