0

I am migrating a site to a new location with another ip address.

To smooth the transition for users and avoid as much downtime as possible, I plan to change DNS and then proxy requests from the old to the new server.

But my proxy test in nginx is not working, and I am not able to figure out why. I just get a totally blank page in the browser. There is nothing in the nginx error log. At the other end, there is no incoming request. The below proxy configuration is working fine in our current setup where we proxy from nginx to 3 webservers (loadbalancing) from port 443 to port 80.

I wonder if the trouble could be related to SSL->SSL proxy? Should I proxy 443->443, or 443->80 or 80->443?

test-proxy.example.com -> test.example.com

server {
     listen 12.34.56.78:443;
     server_name test-proxy.example.com;

     ssl on; 
     ssl_certificate /etc/letsencrypt/live/test-proxy.example.com/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/test-proxy.example.com/privkey.pem;
     include /etc/letsencrypt/options-ssl-nginx.conf; 

     location / { 
        proxy_pass  https://test.example.com:443;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;
        proxy_buffering off;
        proxy_set_header        Host                $host;
        proxy_set_header        X-Real-IP           $remote_addr;
        proxy_set_header        X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto:  https;
        proxy_set_header        Connection          "";
        proxy_connect_timeout       600;
        proxy_send_timeout          600;
        proxy_read_timeout          600;
        send_timeout                600;
     }
}   

# test-proxy.example.com 80
server {
     listen 12.34.56.78:80;
     server_name  test-proxy.example.com;

     #Let's Encrypt: Let requests for acme challenge pass 
     include /etc/nginx/acme.conf;

     location / { 
          rewrite ^(.*) https://test-proxy.example.com$1 permanent;
     }
}
Jette
  • 113
  • 5
  • 1
    Why are you using `proxy_set_header Host $host;` line? Do you understand what does it mean? You send the `Host: test-proxy.example.com` with your proxied request which is (most likely) not expected by the `test.example.com` server. Remove it. – Ivan Shatsky Nov 11 '20 at 11:51
  • Removing `proxy_set_header Host $host` did the trick. Thanks :-) I know it passes the information to the proxied server (as per the documentation) ... I have no idea why it prevents the config from working. Is it related to the SSL certificate?.... Please provide an answer, and I'll be happy to accept it. – Jette Nov 11 '20 at 14:38
  • I've finally managed to write an answer to your question :) Sorry, at the time you asked your question I thought giving a short tip to solve the problem is more important than writing a descriptive answer (which takes some time for a non-native English speakers like me). However related questions are continue to be asked, so I decide to write a comprehensive description somewhere I can point other people to, and historically your question was the first one related to the `Host` problem that I'd answered :) – Ivan Shatsky May 12 '22 at 15:14

1 Answers1

1

First of all, the Host HTTP header is used when a web server serves several different domains (consider several server blocks in the the web server configuration file) listening the same TCP port. To decide what exactly server block should handle the request, the web server choose the appropriate block according to the Host header value. That is, the request with the wrong Host header value may not even reach your web backend at all. And even if it would reach your backend, the web app itself may decide to refuse answering such a request or behave incorrectly (for example, it can use the wrong Host value to generate some links that would lead nowhere or request assets that won't be delivered).

The whole shared hosting concept would be impossible without such a mechanism (or we could only have one web server per a single IP address). When the SSL come to scene, with all the traffic became encrypted, the Server Name Indication (SNI) mechanism was invented for allowing web servers to see this information and continue to be able to chose the correct server block even for the SSL-encrypted HTTP traffic. Some very old clients (eg. Android 2.3, IE on Windows XP) do not know how to use the SNI extensions thus being unable to access HTTPS sites served using the shared hosting providers.

Now back to your question. It should be obvious that your remote server expect the Host header to be set as test.example.com. This is exactly what nginx will send by default (using the server name from the proxy_pass directive). However you change it to $host variable value which will be test-proxy.example.com, and this is completely unexpected by your remote server (probably it won't see the proxied request at all).

The commonly used configuration like

server {
    server_name example.com;
    location / {
        proxy_set_header Host $host;
        proxy_pass http://localhost:5000;
    }
}

is working the opposite way - it allows backend web app to see the real domain name example.com instead of localhost:5000 (which will be sent otherwise). In your case it won't do anything good but only harm.

Ivan Shatsky
  • 2,360
  • 2
  • 6
  • 17