When deploying Nginx behind a load balancer that handles SSL termination (like Rackspace Cloud Load Balancers), traditional HTTPS redirection methods can cause redirect loops. This happens because the load balancer communicates with your backend servers over HTTP while presenting HTTPS to end users.
The complete request flow looks like this:
Client → HTTPS → Load Balancer → HTTP → Nginx → ApplicationHere's the proper Nginx server block configuration that works with load balancers:
server { listen 80; server_name mydomain.com www.mydomain.com; # Use X-Forwarded-Proto header to detect original protocol if ($http_x_forwarded_proto = 'http') { return 301 https://mydomain.com$request_uri; } # Additional www removal (optional) if ($host = 'www.mydomain.com') { return 301 https://mydomain.com$request_uri; } # Your regular server configuration ... }
For more complex scenarios, you can use the map directive:
map $http_x_forwarded_proto $proxy_https_redirect { default 0; http 1; } server { listen 80; server_name mydomain.com www.mydomain.com; if ($proxy_https_redirect) { return 301 https://mydomain.com$request_uri; } if ($host = 'www.mydomain.com') { return 301 https://mydomain.com$request_uri; } ... }
1. Always test with curl -v to verify no redirect loops occur
2. Ensure your load balancer passes X-Forwarded-Proto header
3. Consider adding HSTS header for additional securityHere's a complete configuration we use in production:
# http://mydomain.com and http://www.mydomain.com → https://mydomain.com server { listen 80; server_name mydomain.com www.mydomain.com; # Cloudflare style approach to protocol and domain normalization if ($http_x_forwarded_proto != "https") { return 301 https://mydomain.com$request_uri; } if ($host != "mydomain.com") { return 301 https://mydomain.com$request_uri; } # Rest of your configuration root /var/www/html; index index.php; ... }
When using a cloud load balancer like Rackspace that terminates SSL at the LB level, traditional Nginx HTTPS redirection methods can create infinite loops. Here's why:
# Problematic configuration (creates redirect loop) server { listen 80; server_name example.com www.example.com; return 301 https://example.com$request_uri; }
The load balancer communicates with your backend servers over HTTP (port 80), even when the original request was HTTPS. This means:
- Client → HTTPS → Load Balancer → HTTP → Nginx
- Nginx sees all traffic as HTTP
- Standard redirects create loops
Modern load balancers pass the original protocol via headers. We can use this to implement proper redirects:
server {
listen 80;
server_name example.com www.example.com;
# Check the forwarded protocol header
if ($http_x_forwarded_proto != "https") {
return 301 https://example.com$request_uri;
}
# Your regular configuration
root /var/www/html;
index index.php;
}
We can handle both redirects in a single efficient block:
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
if ($http_x_forwarded_proto != "https") {
return 301 https://example.com$request_uri;
}
# Rest of your configuration
}
For more complex setups, consider using a map directive:
map $http_x_forwarded_proto $proxy_https {
default "off";
"https" "on";
}
server {
listen 80;
server_name example.com www.example.com;
if ($proxy_https = "off") {
return 301 https://example.com$request_uri;
}
}
Always test redirects thoroughly:
curl -I http://example.com
curl -I http://www.example.com
curl -I -H "X-Forwarded-Proto: https" http://example.com