Nginx reverse proxy with dynamic port forwarding

I'm setting up a reverse proxy on Nginx. I need it to listen to multiple ports. I then would like to hit the exact same port on the backend server. Like this: http://frontendserver:9000 -> http://backendserver:9000.

Here's what I thought would work

   ## server configuration
    server {

        listen 9000 ;
        listen 9001 ;
        listen 9002 ;
        listen 9003 ;
        listen 9004 ;
        listen 9005 ;
        listen 9006 ;
        listen 9007 ;
        listen 9008 ;
        listen 9009 ;

        server_name frontendserver;

        if ($http_x_forwarded_proto = '') {
            set $http_x_forwarded_proto  $scheme;
        }

        location / {
                proxy_read_timeout  900;
                proxy_pass_header   Server;
                proxy_cookie_path ~*^/.* /;
                proxy_pass         http://backendserver:$server_port/;
                proxy_set_header    X-Forwarded-Port  $server_port;
                proxy_set_header    X-Forwarded-Proto $http_x_forwarded_proto;
                proxy_set_header    Host              $http_host;
                proxy_set_header    X-Forwarded-For   $proxy_add_x_forwarded_for;
        }
    }

but, it gives me a 502 Bad Gateway error. Any clues why this is, or if there is another way of doing this that would work as explained above?

If i change:

proxy_pass         http://backendserver:$server_port/;

to

proxy_pass         http://backendserver:9000/;

it works just fine, that of course defeats the purpose...


I prepared the directories /var/www/9000 which only contains one file index.html with content of the port corresponding to the path name.

server {
        listen 81;
        server_name example.com;
        location ~ /([^/]+) {
                proxy_set_header Host '10.20.30.40';
                proxy_pass http://10.20.30.40:$1/;
        }
}
server {
        listen 9000;
        server_name default;
        root /var/www/9000;
        try_files $uri $uri/index.html;
}
server {
        listen 9001;
        server_name default;
        root /var/www/9001;
        try_files $uri $uri/index.html;
}
server {
        listen 9002;
        server_name default;
        root /var/www/9002;
        try_files $uri $uri/index.html;
}

I edited my hosts file to contain my server's IP 10.20.30.40 exmaple.com so I can test without the need on a DNS server.
Then I opened my browser with the address http://example.com:81/9000. Content shows corresponding to the port number I supply through the location.

Then I thought it might be a problem with the $server_name variable so I made a second test.

server {
        listen 82;
        server_name example.com;
        location / {
                proxy_set_header Host '10.20.30.40';
                proxy_pass http://10.20.30.40:90$server_port/;
        }
}
server {
        listen 9082;
        server_name default;
        root /var/www/9082;
        try_files $uri $uri/index.html;
}

Now I'm opening my browser with http://example.com:82 and content shows 9082 as expected.

If you supply anything other than the upstream group name it gets treated as domain name. That's when you get the Bad gateway error.

Here is your solution:
You need to use map to translate the requested port to an upsteam group and use this in the proxy_pass.

upstream mycustombackend9082 {
        server 10.20.10.48:9082;
}
map $server_port $backendname {
        82      mycustombackend9082;
}
server {
        listen 82;
        server_name example.com;
        location / {
                proxy_set_header Host '10.20.30.40';
                proxy_pass http://$backendname;
        }
}
server {
        listen 9082;
        server_name default;
        root /var/www/9082;
        try_files $uri $uri/index.html;
}

In my case I needed to write 82 into the map because I can't open the port two times.
You can see another example here.