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 發佈!
相關文章
相關標籤/搜索