Search code examples
nginxnginx-locationnginx-reverse-proxy

Using nginx map directive to dynamically set proxy upstream


I'm trying to make my Nginx a bit more dry, as it's acting as a reverse proxy for nearly 20 servers. Here's what I'm trying to do, all the hostnames and stuff are changed/examples:

map $http_host $backend {
    baz.mydomain.com       hostname1:8080;
    foo.mydomain.com       192.168.1.10:8081;
    bar.mydomain.com       hostname2:1234;
    ham.mydomain.com       hostname2:5678;
}

server {
    listen                      443 ssl http2;
    server_name                 .mydomain.com;

    ssl_certificate             /usr/share/nginx/certs/mydomain.com.pem;
    ssl_certificate_key         /usr/share/nginx/certs/mydomain.com.key;

    location / {
        proxy_redirect          http:// https://;
        proxy_pass              http://$backend;
    }
}

The problem is that no matter what, this will always give a bad gateway error. I've tried a few variations and moving things around, with and without the wildcard server_name, with $host instead of $http_host but so far I can't get it working. Am I even going about this the right way? I'd really prefer not to have almost 20 separate virtual server entries in my config.

There isn't a whole lot of help in the nginx documentation about using map like this, and not a lot online except for one really old post that briefly mentioned something similar here: https://serverfault.com/questions/342309/how-to-write-a-dry-modular-nginx-conf-reverse-proxy-with-named-locations


Solution

  • I got it figured out. The issue was that it didn't like having hostnames in the list. The hostnames are needed as all these addresses are allocated dynamically. This was solved with the upstream directive as follows:

    upstream bazhost {server hostname1:8080;}
    upstream foohost {server 192.168.1.10:8081;}
    upstream barhost {server hostname2:1234;}
    upstream hamhost {server hostname2:5678;}
    
    map $http_host $backend {
        baz.mydomain.com       bazhost;
        foo.mydomain.com       foohost;
        bar.mydomain.com       barhost;
        ham.mydomain.com       hamhost;
    }
    
    server {
        listen                      443 ssl http2;
        server_name                 .mydomain.com;
    
        ssl_certificate             /usr/share/nginx/certs/mydomain.com.pem;
        ssl_certificate_key         /usr/share/nginx/certs/mydomain.com.key;
    
        location / {
            proxy_redirect          http:// https://;
            proxy_pass              http://$backend;
        }
    }