1

I have the following setup, but see 302 Moved Temporarily when I curl the load balancer. In a browser it reports Too Many Redirects:

  • 1 load balancer with SSL
  • 2 web servers

I request https://www.domain.com and I get back a 302 with the same URL in it (see edit below). Here's the server setup:

Server 1 - IP eg 1.1.1.1 Nginx configured as a load balancer which handles SSL and redirects http to https:

server_tokens off; # for security-by-obscurity: stop displaying nginx version
upstream www_backend {
    ip_hash;
    server 2.2.2.2:80;
    server 3.3.3.3:80;
}
# this section is needed to proxy web-socket connections
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
# HTTP
server {
    listen 80;
    server_name www.domain.com; # the domain on which we want to host the application.
    # redirect non-SSL to SSL
    location / {
        rewrite     ^ https://$server_name$request_uri? permanent;
    }
}
server {
    listen 443 ssl spdy;
    server_name www.domain.com;
    ssl on;
    ssl_stapling on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;
    ssl_certificate         /etc/nginx/ssl/www.domain.com/server.pem; # full path to SSL certificate and CA certificate concatenated together
    ssl_certificate_key     /etc/nginx/ssl/www.domain.com/server.key; # full path to SSL key
    ssl_prefer_server_ciphers       on;
    ssl_protocols                   TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDHE-R blah blah';
    # config to enable HSTS(HTTP Strict Transport Security) https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
    # to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
    add_header Strict-Transport-Security "max-age=31536000";
    ssl_dhparam /etc/nginx/ssl/dhparams.pem;
    location / {
        proxy_pass http://www_backend;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_headers_hash_bucket_size 128;
    }
}

Web Server 1 - IP eg 2.2.2.2 Nginx configured as proxy, listens on port 80 and passes on to port 8080:

server_tokens off; # for security-by-obscurity: stop displaying nginx version
# this section is needed to proxy web-socket connections
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
# HTTP
server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;

    server_name www.domain.com;

    root /var/www/nginx/html;

    location /200.html {
        rewrite ^ /200.html break;
    }

    # pass all requests to Meteor
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header X-NginX-Proxy true;
        proxy_redirect off;
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header X-Forwarded-For $remote_addr; # preserve client IP
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_headers_hash_bucket_size 128;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade; # allow websockets
        proxy_set_header Connection $connection_upgrade;

        # this setting allows the browser to cache the application in a way compatible with Meteor
        # on every application update the name of CSS and JS file is different, so they can be cache infinitely (here: 30 days)
        # the root path (/) MUST NOT be cached
        if ($uri != '/') {
            expires 30d;
        }
    }

    error_page 500 502 503 504 /50x.html;
    location /50x.html{
        root /var/www/nginx/html;
    }
}

I've included some static HTML pages to test that the web server is reached.

Meteor application, listens on local port 8080:

export PORT=8080
# this allows Meteor to figure out correct IP address of visitors
export HTTP_FORWARDED_COUNT=1
# The domain name as configured previously as server_name in nginx
export ROOT_URL=http://www.domain.com
#Start Meteor
exec node /var/www/blah/main.js >> /var/log/blah/meteor.log

Web server 2 is configured similarly.

If I ssh into web1 and curl meteor, it responds as expected: curl http:127.0.0.1:8080

If I curl the test HTML page from my dev machine it responds as expected: curl https://www.domain.com/200.html

However if I curl the root from my dev it responds with nothing at all: curl https://www.domain.com

Hitting https://www.domain.com in Chrome show a Too Many Redirect error. As I'm hitting https, not http, I don't understand how these redirects could be caused.

I have A+ rating for the SSL from Qualsys. I've run sudo nginx -t on all server and the nginx files are correct.

I've created a symbolic link from /etc/nginx/sites-enabled/www.domain.com -> /etc/nginx/sites-available/www.domain.com

I've reloaded Nginx: sudo nginx -s reload

Edit BTW - I previously removed /etc/nginx/sites-enabled/default and /etc/nginx/sites-available/default at the recommendation of another ServerFault user.

Edit Here's the verbose response from curl:

* About to connect() to www.domain.com port 443 (#0)
*   Trying 1.1.1.1...
* connected
* Connected to www.domain.com (1.1.1.1) port 443 (#0)
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using DHE-RSA-AES128-SHA
* Server certificate:
*    subject: CN=www.domain.com
*    start date: 2016-03-14 00:00:00 GMT
*    expire date: 2019-03-14 23:59:59 GMT
*    subjectAltName: www.domain.com matched
*    issuer: C=US; O=GeoTrust Inc.; CN=Blah Blah 
*    SSL certificate verify ok.
> GET / HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5
> Host: www.domain.com
> Accept: */*
> 
< HTTP/1.1 302 Moved Temporarily
< Server: nginx
< Date: Mon, 28 Mar 2016 05:52:35 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
< Location: https://www.domain.com/
< Strict-Transport-Security: max-age=31536000
< 
* Connection #0 to host www.domain.com left intact
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):

I checked the Nginx access log on the webserver and seems to confirm that it's the webserver sending the 302, not the load balancer:

[28/Mar/2016:01:56:28 -0500] "GET / HTTP/1.1" 302 5 "-" "curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5"

So in summary, the load balancer seems to be successfully passing the SSL call from 443 to the webservers 80. The meteor service is running on web 1&2 and successfully reponding to local port 8080 requests. It seems that the web servers proxy is not passing port 80 requests on to port 8080.

0

On the web server I changed the scheme line to force https:

proxy_set_header X-Forwarded-Proto https;

sudo reboot and it worked.

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.