NGINX – Fix Virtual Host Routing with Colon in Host Header

httpnginx

My nginx.conf has several "server" sections, and a catch-all server section. Here is an example nginx.conf to give you an idea:

user www-data;
worker_processes auto;
worker_cpu_affinity auto;
pid /run/nginx.pid;

events {
    worker_connections 4000;
    use epoll;
    accept_mutex off;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    error_log /var/log/nginx/error.log;

    server {
        listen 80;

        server_name foo.com;

        location / {
            default_type text/plain;
            return 200 "hello from foo.com";
        }

        error_page 500 502 503 504 /500.html;
    }

    server {
        listen 80 default_server;
        server_name _;

        location / {
            return 403 "sorry";
        }
    }

}

I'm expecting the server to return 403 if the "Host" header is anything but "foo.com".

Somebody's apparently running Burp Suite on my server, and I noticed and interesting behavior when they send a "Host: foo.com:more-stuff-here" header: NGINX routes the request to the first "server" section. It looks as if it ignores the colon and everything after it in the header value.

I can reproduce it locally with the above nginx.conf:

$ curl -H "Host: foo.com" http://127.0.0.1
hello from foo.com

$ curl -H "Host: foo.com:unexpected-content" http://127.0.0.1
hello from foo.com

$ curl -H "Host: bar.com" http://127.0.0.1
sorry

Why does NGINX do this? Is this an expected behavior? What should I change in nginx.conf to ensure requests with "Host: foo.com:more-stuff-here" header go to the default block?

Update: For anybody researching the same issue, I also created a ticket in NGINX issue tracker.

Best Answer

The definition for the host header in the HTTP RFC indicates that the Host header should have the form host:port, with the :port being optional.

nginx sees everything after the colon as the port for the host, but it is irrelevant in your context since you have not specified a server block that way. So it uses the closest match it can find, the host without the "port".