How to Properly Configure HTTP to HTTPS Redirect in Nginx Behind a Load Balancer While Removing WWW


3 views


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 → Application

Here'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 security

Here'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