I run several docker containers with hostnames:
web1.local web2.local web3.local
Routing to these done based on hostname by nginx. I have a proxy in front of this setup (on different machine connected to internet) where I define upstream as:
upstream main {
server web1.local:80;
server web2.local:80;
server web3.local:80;
}
And actual virtual host description:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://main;
}
}
Now, because containers receive hostname "main" instead of "web1.local", they do not respond properly to the request.
Question: how I can tell nginx to pass name of the upstream server instead of name of upstream group of servers in Host: header when proxying request?
Actually you can do that via proxy_set_header.
For more details look here: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header or see an example use-case here: https://stackoverflow.com/questions/12847771/configure-nginx-with-proxy-pass
I have included the dynamic approach into your above posted configuration:
Here is an example with a static host name:
I had the same problem and I finally solved it by using two levels of proxy. Here is how you could do for your situation (I think):
As you can see, the trick is to create a local server responding to a particular port that will proxy the server by rewriting the right Host for each servers. Then, you can use this local servers in your upstream and finally use that upstream in the real proxy.
While the goal seems logical, nginx isn't going to change the Host: header to match the upstream. Instead, it treats
upstream
domain names like aCNAME
in DNS - as a way to get to an IP address.The request headers (and body) are fixed before the upstream is selected. The upstream may change mid-request if it's a particular upstream is found to be non-responsive, but the request doesn't change.
We pass in the upstream addr as a separate header like this
What if you tried?
So from reading all documentation for nginx (I could not really parse code for upstream module =( ) I came up with this bastardized solution. Unfortunately this solution does not keep track of failed hosts, but simply select random one and redirect request to it. So I have to setup some kind of monitoring to make sure all backends are running.
Hmm. I have a similar setup, in which I've simply done
The use of
$http_host
(the HTTP Host header from the incoming request) here rather than$host
(the server hostname configuration) causes the same Host header passed by the client to be passed up to the upstream, in my testing.See also https://stackoverflow.com/questions/14352690/change-host-header-in-nginx-reverse-proxy.
As other people already posted using script variable (like $upstream), you can set it however way you like, and that will fix the issue, without additional header hacking.
Proxy Pass handler threat script variables in a different way, if a value is not conditional (does not have $ in the name) is backed to the upstream on configuration phase and use later.
A simple way to omit this issue, and have the most advantages of (free version) upstream would be using something like
Split_Clients
:The above example looks almost the same as upstream. There exists other modules do the mapping, i.e. chash_map_module, but as they are out of the tree you will need to build them by your own, which is not possible for some use-cases/