深度硬核文:Nginx的301重定向處理過程分析

一,序言html

  「晚上九點,辦公室裏煙霧繚繞,工做進度依然沒有什麼進展。王二胖打開了十來個頁面,一篇篇技術文章打開,關閉,Nginx不停的重啓測試,在試過十來篇技術文章中的方案,通過兩個小時的測試以後,王二胖終於找到了一個解決301錯誤跳轉的可行解決方案。時間已經到了晚上十一點多."nginx

  這樣的場景,在咱們的辦公室裏每天可見。互聯網上有不少Nginx 301問題處理方案的錯誤解答,好比自動加斜槓,端口丟失,暴露內部端口號等,極多量的文章基本就是人云亦云,沒有徹底弄明白Nginx如何處理301狀態碼的。甚至對於一些關鍵性的配置信息的解釋是徹底錯誤的。本着源代碼就是最正確的文檔的原則,我閱讀了一遍Nginx處理301問題的相關源代碼。程序員

最終發現,Nginx處理301 Moved Permanently的邏輯至關簡單,只有六種分支。算法

二,Nginx的301狀態碼處理邏輯設計瀏覽器

  讓咱們先看看Nginx的邏輯設計是怎麼樣的。
  HTTP協議中3xx開頭的狀態響應碼都是表示重定向的響應。根據RFC的定義
  301 Moved Permanently
  302 Found
  303 See Other
  307 Temporary Redirect服務器

  301是永久重定向。若是使用Nginx做爲HTTP 服務器,那麼當用戶輸入一個不存在的地址以後,基本上會有兩種狀況,返回404狀態碼,或者301狀態碼。404 Not Found不作討論,只是說下301 Moved Permanently的處理過程。微信

  首先的關鍵問題是:頁面重定向功能會在什麼樣的狀況下被觸發?ide

  答案是:Nginx負責設置301 Moved Permanently狀態碼。但nginx.conf控制Nginx如何處理301 Moved Permanently狀態碼!換句話說,要不要進行頁面重定向,和怎麼重定向,徹底是用戶配置的結果!六種分支的選擇徹底是根據用戶的配置來決定的。測試

  根據源代碼,Nginx的算法邏輯設計是分紅兩個部分的。第一部分是設置狀態碼,第二部分是對應狀態碼的實際響應處理。url

  Nginx和瀏覽器之間的通信過程,好比一次正常的HTTP 訪問過程,以下圖。

file

  從邏輯順序上來講,Nginx會先設置好狀態碼,而後根據狀態碼來構造Response Header和Body,最後發送給瀏覽器,讓瀏覽器渲染頁面內容。

  301 Moved Permanently狀態碼和200 OK狀態碼的處理過程是一致的。Nginx主動設置301 Moved Permanently狀態碼只有一種狀況,當用戶輸入了一個url地址,最後的部分是一個文件目錄。好比 http://www.test.com/index, Nginx在運行過程當中沒有找到index這個文件,但發現了index是個目錄。因而本次訪問的狀態碼就會被設置成301 Moved Permanently。

file

  但注意!設置成301 Moved Permanently,不必定會致使瀏覽器重定向。從HTTP定義來講,致使瀏覽器觸發重定向操做是由於瀏覽器收到了一個Location response header;

  讓咱們來看看Location的定義說明,很明確的說明了Location的做用。

The Location response header indicates the URL to redirect a page to. It only provides a meaning when served with a 3xx (redirection) or 201 (created) status response.

  Nginx在Response Header裏寫入一個Location以後。瀏覽器能夠根據Location來控制重定向過程。邏輯過程如上圖。並且nginx.conf文件中的配置將影響到Location URL的生成方式。

三,nginx.conf中配置項的做用

  nginx.conf文件在哪一個環節起做用呢?答案就是設置Location以前。
file

  通常狀況下,咱們會在nginx.conf中配置absolute_redirect ,server_name_in_redirect和port_in_redirect,以便到達個性化的重定向功能。這三個配置項的做用是不少人明白的,但對於邏輯順序不多有文章提到。

四,重定向三配置
  absolute_redirect ,server_name_in_redirect和port_in_redirect三個設置項中,根據Nginx的源代碼中Response Header處理算法邏輯,Nginx可以控制重定向的關鍵配置項是:absolute_redirect,在整個Nginx代碼中,absolute_redirect在控制在Response Header如何增長Location url。

  absolute_redirect設置成On,則生成absolute url做爲Location url。
  absolute_redirect設置成Off,則生成relative url做爲 Location url。

  absolute url是包含完整信息的url,好比http://www.test.com:8080/index/1.html 這樣的URL地址

  relative url 則省略了服務器名字和端口號,好比 /index/1.html

  由於relative url沒有端口號,沒有Host名字,因此absolute_redirect 設置On的時候,server_name_in_redirect和port_in_redirect兩項設置纔會起做用。

  我花了點時間仔細閱讀了Nginx的相關源代碼,並畫了流程圖。

file

  從以上邏輯過程, absolute_redirect,server_name_in_redirect,port_in_redirect 三項配置,共同控制了生成字符串 「Location: http://server_name:port/test/」的結果。

  server_name_in_redirect 控制URL中的Server Name,也就是Host使用哪一個值,port_in_redirect控制URL中的port是使用哪一個值。經過這三個配置項,最終決定了Nginx返回給瀏覽器的Location內容。

  用MindMap來表達就是

file
  備註:header_in.server是nginx源代碼中的變量,指向用戶輸入的url的服務器名字部分。
  根據以上腦圖,咱們能夠很清楚的看到,最終咱們只有六個分支結果。

五,案例分析
  依據上面的分析,咱們具體舉個很是疑難的例子,看看如何解決問題。

  1,問題:http://www.test.com:888/index 被錯誤的重定向至 http://www.test.com/index/
這種狀況多見於使用NAT作端口映射,或者是用容器來運行Nginx。內部服務器或者容器中nginx監聽的是80端口號

  而咱們的指望答案是:http://www.test.com:888/index 重定向至 http://www.test.com:888/index/

  2,分析和解答:
  假設Nginx使用默認設置
  absolute_redirect:on
  server_name_in_redirect:off
  port_in_redirect :on

  這個問題看似丟失了端口號,其實是暴露了nginx的內部端口號。這種狀況下,port_in_redirect無論設置成on或者off都不會起做用。

  port_in_redirect = on,nginx監聽80端口,默認80端口號不須要在url中設置【3號分支】,結果是http://www.test.com/index/

  port_in_redirect = off,nginx不設置port數據【5號分支】,結果仍是http://www.test.com/index/

  咱們會發現,在這裏不管怎麼調整server_name_in_redirect【1,2號分支】和port_in_redirect的on,off都是無效的。由於1號至5號分支,都沒有包含這種狀況。 解決問題的方向錯了,解決方案固然就是錯誤的。

  實際的解決方案有兩個:

  rewrite 【分支之外】
  absolute_redirect:off 【六號分支】

  rewrite是經過腳原本控制nginx的運行過程,不在上面的配置分支中。具體操做能夠參考下面的設置

  rewrite 1$ $scheme://$http_host$uri/ permanent;

  備註:rewrite的操做方式依據不一樣的目錄結構,可能略有不一樣,請根據實際狀況來設置。

  absolute_redirect:off,就是六號分支
  absolute_redirect:off以後,Nginx返回Location:/index/,但這個方式也許會帶來其餘問題。

六, 結語:

  文章分析到這裏基本上已經沒有什麼內容好說的了。上面的思惟導圖已經把全部的解決問題的分支所有展示出來了。
但最後,有感於互聯網上中文技術文章的抄襲現象,我想說源代碼就是最好的文檔,做爲程序員不要人云亦云。
  有問題,多看源代碼!

七,參考資料:

  nginx源代碼
  srchttpv2ngx_http_v2_filter_module.c
  srchttpngx_http_header_filter_module.c

謝謝閱讀
本文完成於2019-09-26


  寫多不如寫精!
  我是丁長老,十多年開發經驗。
  歡迎關注個人技術公衆號。
  我的微信號nine-ding,歡迎加我微信瞎聊。
  如需轉發文章請加微信號獲取轉發受權。


  1. /
相關文章
相關標籤/搜索