我运行了几个具有主机名的docker容器:

web1.local
web2.local
web3.local

将它们路由到nginx基于主机名完成的路由。我在此设置(在连接到Internet的另一台计算机上)之前有一个代理,在其中我将上游定义为:

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


以及实际的虚拟主机描述:
现在,由于容器收到的主机名是“ main”而不是“ web1.local”,因此它们无法正确响应请求。

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

评论

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

正确的做法是修复应用程序。

@MichaelHampton在某些情况下这是不可能的,例如,如果对TLS SNI使用proxy_ssl_server_name,则它需要正确的服务器名称。

恕我直言,这是Nginx中的错误。上游的名称“ main”只是.conf文件中的本地引用,不需要反映DNS可以解析的或后端已知的实际主机名。基本上,除非您的后端知道此引用或对主机做出响应:*您不能使用Nginx的上游指令。

#1 楼

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

有关更多详细信息,请点击此处:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header或在此处查看示例用例:https://stackoverflow.com/ Questions / 12847771 / configure-nginx-with-proxy-pass

我已将动态方法纳入上述发布的配置中:

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;
  }
}


评论


proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for;似乎更好

– sivann
2014年5月24日9:00

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

–詹斯·布拉德勒
2014年5月25日晚上11:37

@JensBradler您似乎比我更专家,您能告诉我您对我的解决方案的看法吗?我想做同样的事情,因为我从ISP上的两个帐户运行了网站的两个副本:site1.myisp.com和site2.myisp.com,它们仅响应各自的名称。我现在拥有我的域名,我想使用我的ISP网站来平衡服务器的负载。那不是很好的理由吗?非常感谢你 ;)

–ncenerar
2014年8月20日15:45

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

–詹斯·布拉德勒
2014-09-24 18:28

该答案反映了官方博客的建议。

–伯纳德·罗塞(Bernard Rosset)
16年4月4日在16:05

#2 楼

我遇到了同样的问题,最后我通过使用两个级别的代理解决了这个问题。这是针对您的情况的方法(我认为):

 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
2014年8月20日在18:58

我不知道这会如何影响性能(添加额外的服务器{.. proxy_pass ...}层)

– KajMagnus
20年6月7日在20:33



#3 楼

尽管目标似乎合乎逻辑,但nginx不会更改Host:标头以匹配上游。而是像对待DNS中的upstream一样对待CNAME域名-作为一种获取IP地址的方法。

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

#4 楼

因此,通过阅读有关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;
        }
}


评论


如果使用此方法关闭上游主机会怎样?

–祖格里布
20年11月5日,0:46

#5 楼

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

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;
  }
}


#6 楼

嗯我有一个类似的设置,其中我只是在这里完成了

location / {
    ... 
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_pass ...;
}


在这里使用$http_host(传入请求的HTTP主机头)而不是$host(服务器主机名配置)在我的测试中导致客户端传递的同一Host标头传递到上游。

另请参阅https://stackoverflow.com/questions/14352690/change-host -header-in-nginx-反向代理。

评论


根据docs nginx.org/en/docs/http/…,$host变量-其值等于“主机”请求标头字段中的服务器名称,或者等于该字段不存在时的主服务器名称-因此可能是$ host更好

– keypress
20-2-13在16:21



#7 楼

正如其他人已经使用脚本变量(例如$ 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,但是由于它们不在树中,因此您需要自己构建它们,这在某些用例/
中是不可能的