使nginx在反向代理时传递上游的主机名


89

我使用主机名运行几个Docker容器:

web1.local web2.local web3.local

这些路由由nginx根据主机名完成。我在此设置之前(在连接到互联网的另一台计算机上)有一个代理,在其中我将上游定义为:

    upstream main {
      server web1.local:80;
      server web2.local:80;
      server web3.local:80;
    }

以及实际的虚拟主机说明:

    server {
      listen 80;
      server_name example.com;
      location / {
        proxy_pass http://main;
      }
    }

现在,由于容器收到的主机名是“ main”而不是“ web1.local”,因此它们无法正确响应请求。

问题:代理请求时,如何告诉nginx在Host:标头中传递上游服务器的名称而不是上游服务器组的名称?


3
我认为你不能。您为什么不将后端服务器设置为响应main或example.com?这并不是因为如果后端不知道谁是。反过来很容易实现:proxy_set_header主机$ host; 将从上游返回的任何Host变量替换为原始请求的主机名。
Andrew Domaszek

正确的做法是修复应用程序。
迈克尔·汉普顿

Answers:


109

实际上,您可以通过proxy_set_header做到这一点。

有关更多详细信息,请访问此处:http : //nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header或在此处查看示例用例:https : //stackoverflow.com/questions/12847771/configure-nginx-代理通过

我已经将动态方法包含在您上面发布的配置中:

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

这是一个带有静态主机名的示例:

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            www.example.com;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

7
proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for; 似乎更好
sivann 2014年

1
@pavel:知道了。其实我也做了一些研究和测试。似乎没有直接的方法可以满足您的要求。因此,即使是“受重创”的解决方案也是解决方案。我不想问你为什么要这样做。我很确定你有你的理由。:-)
延斯·布拉德勒

@JensBradler您似乎比我更专家,您能告诉我您对我的解决方案的看法吗?我想是因为我从我的ISP两个帐户运行我的网站的两个副本做相同的:site1.myisp.comsite2.myisp.com他们只是各自的名称作出回应。我现在拥有我的域名,并且我想使用我的ISP网站来平衡服务器的负载。那不是很好的理由吗?非常感谢;)
ncenerar 2014年

1
@ncenerar您可以执行此操作,但这将导致单点故障:负载均衡器。如果这是用于负载平衡(不是冗余),则还可以将基于DNS的负载平衡与DNS故障转移结合使用。
詹斯·布拉德勒


28

我遇到了同样的问题,最后我通过使用两个级别的代理解决了这个问题。您可以根据自己的情况采取以下措施(我认为):

server {
  listen      8001 default_server;
  server_name web1.example.com;
  location / {
    proxy_pass       http://web1.local:80;
    proxy_set_header Host web1.local:80;
  }
}

server {
  listen      8002 default_server;
  server_name web2.example.com;
  location / {
    proxy_pass       http://web2.local:80;
    proxy_set_header Host web2.local:80;
  }
}

server {
  listen      8003 default_server;
  server_name web3.example.com;
  location / {
    proxy_pass       http://web3.local:80;
    proxy_set_header Host web3.local:80;
  }
}

upstream main {
  server 127.0.0.1:8001;
  server 127.0.0.1:8002;
  server 127.0.0.1:8003;
}

server {
  listen      80;
  server_name example.com;
  location / {
    proxy_pass http://main;
  }
}

如您所见,诀窍是创建一个响应特定端口的本地服务器,该服务器将通过为每个服务器重写正确的主机来代理该服务器。然后,您可以在上游使用此本地服务器,最后在真实代理中使用该本地服务器。


我最初使用Lua方法,但现在完全切换到HAProxy,它可以使用标准配置执行我想做的事情。
pavel_karoukin

3

因此,通过阅读有关nginx的所有文档(我无法真正解析上游模块=()的代码,我想到了这个混蛋解决方案。不幸的是,该解决方案无法跟踪失败的主机,而只是选择随机的主机并将请求重定向到该主机。因此,我必须设置某种监视以确保所有后端都在运行。

server {
        listen 80;
        server_name example.com;
        resolver 127.0.0.1;

        location / {
                set $upstream "";
                rewrite_by_lua '
                        local upstreams = {
                                "http://web1.dokku.localdomain",
                                "http://web2.dokku.localdomain",
                                "http://web3.dokku.localdomain",
                                "http://web4.dokku.localdomain"
                        }
                        ngx.var.upstream = upstreams[ math.random( #upstreams ) ] 
                ';
                proxy_pass $upstream;
        }
}

2

我们像这样将上游地址作为单独的标头传入

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Upstream      $upstream_addr;
  }
}

如果您尝试了怎么办?

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $upstream_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Host          $host;
  }
}

2

尽管目标似乎合乎逻辑,但nginx不会更改Host:标头以匹配上游。相反,它将upstream域名视为CNAMEDNS中的域名-作为获取IP地址的一种方式。

在选择上游之前,请求标头(和正文)是固定的。如果发现上游特定上游没有响应,则上游可能会更改中间请求,但请求不会改变。



0

正如其他人已经使用脚本变量(例如$ upstream)发布的那样,您可以按自己喜欢的方式进行设置,这样就可以解决此问题,而无需其他标题攻击。

如果值不是条件的(名称中没有$),则以不同的方式将Proxy Pass处理程序威胁脚本变量返回到配置阶段的上游,并在以后使用。

一个简单的方法忽略这个问题,有(免费版)中最上游的优势将使用类似Split_Clients

split_clients $request_uri $my_upstream {
              33%          server1.domainX.com;
              33%          server2.domainX.com;
# Always use DOT at end entry if you wonder why, read the SC code.
              *            server3.domainX.com;  
}
location / {
    ... 
    proxy_pass http://$my_upstream;
}

上面的示例与上游看起来几乎相同。还有其他模块可以进行映射,即chash_map_module,但是由于它们不在树中,因此您需要自己构建它们,这在某些用例/中是不可能的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.