Apache和Nginx自定义修改Response Header中的Location值

1. 原因

最近单位里面调整应用端口和防火墙配置,原先起在 80 端口上的服务,须要调整到 8180 端口上进行部署。为了对公网的影响度最小,公网端口依旧采用的 80 端口暴露服务,由网络的同事在 WAF 侧作了一个 80 到 8180 端口的映射。造成了一个这样的网络拓扑(脱敏简化)图。html

简单网络拓扑图

造成这样的网络拓扑以后,按理公网上访问 http://test.snowheart.cn/ 时便可正常访问到后台的应用服务。But,后端有一些地方用到了 sendRedirect(url) 方法,这就致使了在进行 302 网络跳转的时候,Response 的 Location Header 上面带了内部的端口,即造成了下述形式的 Location Header。java

HTTP/1.1 302 Moved Temporarily
Date: Mon, 11 May 2020 15:45:29 GMT
Location: http://test.snowheart.cn:8180/app/login.jsp
Content-Type: text/html;charset=utf-8
Connection: close
Transfer-Encoding: chunked

浏览器在进行跳转时,使用了前面一个请求的 Location,在公网上请求了 8180 端口,由于防火墙的缘故,没法访问,报错。nginx

2. 问题分析

2.1. 浏览器的重定向跳起色制

在 HTTP 的 Code 响应码中,301 表示永久重定向,302 表示临时重定向。git

  • 经过 Response 中的 HTTP Code,浏览器能够得知接下来要进行再次请求跳转了。
  • 经过 Response 中的 Location,浏览器能够得知下一次要请求到哪里。
// DemoServlet1
// 经过设置 Header 和 Status,来进行重定向操做,其余什么都不作
response.setHeader("Location", request.getContextPath() + "/hello.html");
response.setStatus(302);

上述例子中的 Servlet URL,在浏览器中访问以后,你会发现浏览器被重定向到了项目中的 /hello.html,其代码在实现结果上,等价于下面的代码:github

// DemoServlet2
// 经过 sendRedirect(url) 来进行重定向操做,其余什么都不作
response.sendRedirect(request.getContextPath() + "/hello.html");

2.2. 8180 端口是在哪里添加的?

若是 WAF 上是同端口透明出去了,公网和内网前置机的 Port 一致;shell

链路上除前置机应用端口为 8180 外,其他地方端口均与此无关;后端

只是依赖 Apache 或 Nginx 的反向代理,是不会出现后端 Port 暴露的事情;浏览器

看来是在 WAF 作了端口转换以后,内网前置机的 Apache 并不知道公网上的端口已是 80 了,故在 Location 上增长了 8180 端口的信息;服务器

亦可经过在各环节打印日志的形式,来检测 8180 端口是哪一个环节增长的...

故:咱们断定,在从前置机 Apache 返回时,Location 上就已经有了前面的域名 + 端口,如今须要作的事情就是在返回以前,将 Location 上的端口给抹掉,亦或者是将 Location 上的域名 + 端口给抹掉,仅剩余从 / 开始的 URI 地址。网络

3. 改造实施

最早时候,想经过本身最熟悉的 Nginx 来进行 HTTP Response Header 的重写

3.1. Nginx 配置

  • 在 Linux 虚机上安装 Nginx 并进行测试工做(已有 Nginx 跳过)

    Windows 下的 Nginx 程序包没法进行再次编译,没有办法来加载其余 Nginx 模块;故须要使用 Linux 下的 Nginx 服务,以最大限度地与服务器保持一致。

  • 查询 Nginx 编译参数,安装 ngx_headers_more 模块,从新编译并覆盖 nginx 文件
# 下载最新的 ngx_headers_more 模块包
wget https://github.com/openresty/headers-more-nginx-module/archive/v0.33.tar.gz
tar -xzvf v0.33.tar.gz

# 跳转到 Nginx 目录
# 查看 Nginx 编译参数
./nginx -V

# 跳转至 Nginx 源码目录,增长 ngx_headers_more 配置并编译
# 每一个人的目录都不相同,仅供参考
./configure --prefix=/usr/local/nginx/server --with-http_stub_status_module --with-http_ssl_module --with-pcre=/tool/pcre-8.44 --with-http_realip_module --with-http_sub_module --with-http_gzip_static_module --add-module=/tool/echo-nginx-module-0.62rc1 --add-module=/tool/headers-more-nginx-module-0.33
make
# 切记,不要 make install,不然会覆盖掉以前Nginx的配置文件这些。
cp /usr/local/nginx/server/sbin/nginx /usr/local/nginx/server/sbin/nginx.origin
cp objs/nginx /usr/local/nginx/server/sbin/nginx
  • 修改配置文件 nginx.conf
map $upstream_http_Location $location{
  # 这种方案是把全部到 8180 端口的重定向都改为 80 端口,有必定的风险,容易误伤
  # ~http://(?<domains>.*):8180/(?<param>.*) http://$domains/$param;

  # 这种方案是针对特定域名 8180 端口的重定向,范围可控,写法冗长
  ~http://test.snowheart.cn:8180/(?<param>.*) http://test.snowheart.cn/$param;

  # 默认状况,保持原状
  default $upstream_http_Location;
}

server {
  location /app {
    proxy_pass       http://192.168.36.72:7001/app;
    proxy_set_header Host               $host:$server_port;
    proxy_set_header X-Real-IP          $remote_addr;
    proxy_set_header REMOTE-HOST        $remote_addr;
    proxy_redirect   off;
    proxy_set_header X-Forwarded-Proto  $scheme;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
    more_set_headers -s '301 302' 'Location $location';
  }
}
  • 重启应用,进行测试
$ curl http://test.snowheart.cn/app/RedirectServlet --dump -

HTTP/1.1 302 
Server: nginx/1.14.2
Date: Mon, 11 May 2020 16:41:23 GMT
Content-Length: 0
Location: http://test.snowheart.cn/app/hello.html
Connection: keep-alive

大坑

Nginx 须要安装新的 module,这又涉及到从新编译和 root 安装等操做,在企业里面,作这些是须要必定的流程和规范性评估的,故通过考虑后,放弃此方案。上述笔记仅做为 POC 阶段使用。

Nginx 方案受阻后,使用当前测试环境在用的 Apache 来实现 Location 的重写

3.2. Apache 配置

  • 配置文件
# httpd.conf
# 打开 Rewrite 功能
RewriteEngine On

# 对 Location 进行重写
Header edit Location "(^http[s]?://test.snowheart.cn:8180/)" "/"
Header edit Location "(^http[s]?://123.234.123.234:8180/)" "/"
  • 测试应用
$ curl http://test.snowheart.cn/app/RedirectServlet --dump -

HTTP/1.1 302 
Server: nginx/1.14.2
Date: Mon, 11 May 2020 16:41:23 GMT
Content-Length: 0
Location: http://test.snowheart.cn/app/hello.html
Connection: keep-alive

简单的三行配置,即达成了咱们的目的。

4. 感悟

  • 从问题的实际状况出发,而不是从咱们自觉得的解决方案出发。咱们的目的在于更快更好地解决当下的问题,而不是都一大圈子回来,作不少无谓的工做。
  • 大道至简,每每最简单的配置,最简单的代码,就能够实现咱们想要的复杂的功能点。莫要钻进牛角尖自觉得是出不来。
  • 工做才是最好的老师,学中用,用中学,方能不断进步。
  • 解决问题也不必定须要亲力亲为,在企业内部,有专门的中间件团队和流程规范来保证着专业的人,作专业的事,作正确的事。多交流远比本身琢磨要好得多。

5. 参考连接

本文由博客一文多发平台 OpenWrite 发布!
相关文章
相关标签/搜索