记一次nginx反向代理错误配置-注意域名

217 阅读2分钟

需求:

需求是这样的:有一个 xxx-yyy-zzz-publish.test-d.com.cn/view/scene?… 页面需要用iframe嵌入到 业务系统 ayewu.test.com.cn(192.168.1.8) 里。

分析:

分析了一下:xxx-yyy-zzz-publish.test-d.com.cn 也就是 xxx-yyy-zzz.test-d.com.cn 系统嘛,这个系统摸索过一段时间,很熟悉啊(没想到在这挖了个坑)。业务系统是https协议,而嵌入页面只支持http,嵌入大概率Mixed Content被浏览器拦截。于是考虑使用nginx反向代理,将http代理为https,并挂在 9112 接口上。配置如下相当简单:  

  location / {
    proxy_pass http://xxx-yyy-zzz.test-d.com.cn;
    proxy_cookie_domain ~.* $host; -- 登录后,服务器返回的cookie domian不对,修改一下
  }

登录 ayewu.test.com.cn:9112 测试了一下,没什么问题,系统登录一切正常。

代理配置好后,来了一个优先级更高的任务,于是这个需求暂时被搁置了。一段时间后,继续开始处理。

掉坑里 - 奇怪的301问题:

在 业务系统 ayewu.test.com.cn 上,加上iframe,配置上地址 ayewu.test.com.cn:9112/view/scene?… ,一片空白。F12打开调试器,返回了301错误。

    HTTP/1.1 301 Moved Permanently
    Location: http://zzz-publish?id=123456&publish=1
    <html>
      <head><title>301 Moved Permanently</title></head>
      <body bgcolor="white">
      <center><h1>301 Moved Permanently</h1></center>
      <hr><center>nginx</center>
      </body>
    </html>

于是根据 301返回的Location字段 将地址改为 ayewu.test.com.cn:9112/zzz-publish… ,仍然是301。

为什么 ayewu.test.com.cn:9112 能打开,而单独的页面却不行,难道是页面单独做了限制。回想过去配置反向代理,有时会配置Host,怀疑是没有设置Host,一顿尝试,竟然走通了。

  location / {
    proxy_pass http://xxx-yyy-zzz.test-d.com.cn;
    proxy_cookie_domain ~.* $host; -- 登录后,服务器返回的cookie domian不对,修改一下
    proxy_set_header Host xxx-yyy-zzz-publish.test-d.com.cn;
  }

但是 ayewu.test.com.cn:9112 却打不开了.

思考

为什么加了Host可以了,现象也很奇怪,不太理解,于是测试如下场景:

  1. 不使用代理,直接访问 xxx-yyy-zzz-publish.test-d.com.cn/view/scene?… ,去掉Host就有问题,否则正常
    GET /view/scene?id=275817090068500008&publish=1 HTTP/1.1
    Host: xxx-yyy-zzz-publish.test-d.com.cn
  1. 修改配置,将请求代理到测试环境 192.168.1.3,在测试环境监听接收到的内容
      location / {
        proxy_pass http://192.168.1.3:10022;
        proxy_cookie_domain ~.* $host; -- 登录后,服务器返回的cookie domian不对,修改一下
        proxy_set_header Host xxx; -- 改变此处,测试区别
      }  

2.1. 不带 proxy_set_header Host 时,测试环境接收到的请求:

      GET /view/scene?id=123456&publish=1 HTTP/1.0
      Host: 192.168.1.3:10022  

也就是说不带参数时,Host实际是proxy_pass地址,即xxx-yyy-zzz-publish.test-d.com.cn

2.2. 配置 proxy_set_header Host $proxy_host 时,测试环境接收到的请求:

      GET /view/scene?id=123456&publish=1 HTTP/1.0
      Host: 192.168.1.3:10022

配置 $proxy_host时,Host实际是proxy_pass地址

2.3. 配置 proxy_set_header Host $host 时,测试环境接收到的请求:

      GET /view/scene?id=123456&publish=1 HTTP/1.0
      Host: 192.168.1.8

配置 $host时,Host实际是nginx的ip (直接访问了 ayewu.test.com.cn 系统的 ip地址 192.168.1.8,使用域名结果可能不同)

2.4. 配置 proxy_set_header Host $http_host 时,测试环境接收到的请求:

      GET /view/scene?id=123456&publish=1 HTTP/1.0
      Host: 192.168.1.8:9112

配置 $http_host,Host实际是nginx的ip + 端口

  1. 按照测试结果,2.1 与 1 的效果是一样的。所以,即使不配置Host也应该能走通啊,百思不得其解。

问题的根源

暂时不管,过了一段时间,再次回顾配置。发现 xxx-yyy-zzz-publish.test-d.com.cn, xxx-yyy-zzz.test-d.com.cn 并不是同一个(这是两个不同的服务。test-d.com.cn单独弄了一个publish服务,来对外发布页面)。

为什么修改Host访问就正常了呢?

测试ip地址:

  C:\>ping xxx-yyy-zzz-publish.test-d.com.cn
  正在 Ping xxx-yyy-zzz-publish.test-d.com.cn [192.168.2.7] 具有 32 字节的数据:
  来自 192.168.2.7 的回复: 字节=32 时间=28ms TTL=62

  C:\>ping xxx-yyy-zzz.test-d.com.cn
  正在 Ping xxx-yyy-zzz.test-d.com.cn [192.168.2.7] 具有 32 字节的数据:
  来自 192.168.2.7 的回复: 字节=32 时间=2ms TTL=62

很明显,两个地址的ip其实是相同的,服务端 通过Host来转发给不同的内部服务。

未配置proxy_set_header Host时,proxy_pass起决定作用。publish服务的地址当然也就访问不了。

配置proxy_set_header Host后,proxy_pass虽然是域名,但只起到了提供ip地址的作用,proxy_set_header Host将Host覆盖了,成为真正起决定作用的配置。这也是为什么配置Host后,代理的test-d.com.cn首页就打不开了,此时反向代理实际指向的是publish服务。