7

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

Tal Bull
  • 389
  • 1
  • 2
  • 10

2 Answers2

14

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;
    }
}
Tal Bull
  • 389
  • 1
  • 2
  • 10
1

I figured the details out.
Basically, there is no DNS resolution if we set proxy_pass targets dynamically.

This won't work:

map $http_host $backend {
    default localhost:8080;
}

proxy_pass http://$backend;

But, this work:

map $http_host $backend {
    default 127.0.0.1:8080;
}

proxy_pass http://$backend;
Curious Sam
  • 884
  • 10
  • 12