一篇文章說透Nginx的rewrite模塊

rewrite模塊即ngx_http_rewrite_module模塊,主要功能是改寫請求URI,是Nginx默認安裝的模塊。rewrite模塊會根據PCRE正則匹配重寫URI,而後發起內部跳轉再匹配location,或者直接作30x重定向返回客戶端。nginx

rewrite指令的工做原理

rewrite模塊的指令有break, if, return, rewrite, set等。rewrite指令所執行的順序以下:正則表達式

  • 首先在server上下文中依照順序執行rewrite模塊指令;
  • 若是server中行了rewrite重寫,那麼以新URI發起內部跳轉,直接匹配location,不會再執行server裏的rewrite指令,而後
    • 新URI直接匹配location
    • 若是匹配上某個location,那麼其中的rewrite模塊指令一樣依照順序執行
    • 若是再次致使URI的rewrite,那麼再一次進行內部跳轉去匹配location,但跳轉的總次數不能超過10次

rewrite

基本語法: rewrite regex replacement [flag];
上下文:server, location, if瀏覽器

regex是PCRE 風格的,若是regex匹配URI,那麼URI就會被替換成replacement,replacement 就是新的URI。若是rewrite同一個上下文中有多個這樣的正則,匹配會依照rewrite指令出現的順序前後依次進行下去,匹配到一個以後並不會終止,而是繼續往下匹配,直到返回最後一個匹配上的爲止。若是想要停止繼續往下匹配,可使用第三個參數flag。cookie

若是新URI字符中有關於協議的任何東西,好比http://或者https://等,進一步的處理就終止了,直接返回客戶端302。curl

若是返回的是30x,那麼瀏覽器根據這個狀態碼和Location響應頭再發起一次請求,而後才能獲得想要的響應結果。可是,若是不是返回30x狀態碼,那麼跳轉就是內部的,瀏覽器不作跳轉就能獲得相應。url

注意:regex直接就是正則表達式,不要再前面添加~符號調試

flag 參數能夠有如下的一些值:日誌

last

若是有last參數,那麼中止處理任何rewrite相關的指令,當即用替換後的新URI開始下一輪的location匹配code

break

中止處理任何rewrite的相關指令,就如同break 指令自己同樣。server

last的break的相同點在於,當即中止執行全部當前上下文的rewrite模塊指令;不一樣點在於last參數接着用新的URI立刻搜尋新的location,而break不會搜尋新的location,直接用這個新的URI來處理請求,這樣能避免重複rewite。所以,在server上下文中使用last,而在location上下文中使用break

redirect

replacement 若是不包含協議,仍然是一個新的的URI,那麼就用新的URI匹配的location去處理請求,不會返回30x跳轉。可是redirect參數可讓這種狀況也返回30x(默認302)狀態碼,就像新的URI包含http://和https://等同樣。這樣的話,瀏覽器看到302,就會再發起一次請求,真正返回響應結果的就是這第二個請求。

permanent

和redirect參數同樣,只不過直接返回301永久重定向

雖然說URI有了新的,可是要拼接成完整的URL還須要當前請求的scheme,以及由server_name_in_redirectport_in_redirect指令決定的HOST和PORT.

還有一個比較有意思的應用,就是若是replacement中包含請求參數,那麼默認狀況下舊URI中的請求參數也會拼接在replacement後面做爲新的URI,若是不想這麼作,能夠在replacement的最後面加上?。

舉例說明:

rewrite ^/users/(.*)$ /show?user=$1? last;

這樣的新URI仍是 /show?user=xxx

但若是不加問號:

rewrite ^/users/(.*)$ /show?user=$1 last;

獲得的新URI就是/show?user=$1&xxx=xxx。其中xxx=xxx是舊URI所帶的請求參數。

rewrite的例子

在server中使用的狀況:

server {
    ...
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra  last;
    return  403; #沒有匹配上,那就返回403咯
    ...
}

注意,在server中使用rewrite ,咱們使用的flag是last,可是在location中,咱們卻只能用break:

location /download/ {
    rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;
    rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra  break;
    return  403;
}

若是在location的rewrite也使用last,便會再次以新的URI從新發起內部重定向,再次進行location匹配,而新的URI中極有可能和舊的URI同樣再次匹配到相同location中,這樣死循環發生了。當循環到第10次時,Nginx會終止這樣無心義的循環,並返回500錯誤。這點須要特別的注意。

break

基本語法:break;
上下文:server, location, if

中止處理任何rewrite的相關指令。若是出如今location裏面,那麼全部後面的rewrite模塊指令都不會再執行,也不發起內部重定向,而是直接用新的URI進一步處理請求。

if

基本語法:if (condition) { ... }
上下文:server, location

根據條件condition的真假決定是否加載{...}中的配置,{...}中的配置能夠繼承外面的配置,也能夠對外面已有配置指令進行覆寫。

條件condition是針對變量而言的,變量既能夠是系統變量,也能夠是自定義的,能夠是下面幾種狀況:

  1. 當condition爲變量$var自己時,當且僅當變量值爲空字符或者0時,條件爲false,其他狀況皆爲 true
  2. 變量$var經過"="或者"!="與字符串相比較,即$var = xxx或者$var != xxx
  3. 匹配一個正則表達式
  4. -f -d -e -x等檢驗文件或者目錄屬性或者存在與否的運算符

正則匹配

其中,對於 3. 匹配一個正則表達式的狀況,能夠細分爲:

  • $var ~ Reg 表示大小寫敏感匹配
  • $var ~* Reg 表示大小寫不敏感匹配
  • $var !~ Reg 表示大小寫敏感不匹配
  • $var !~* Reg 表示大小寫不敏感不匹配

Reg 中能夠捕獲變量,當其中包含有 } 或者 ; 時須要用雙引號或者單引號括起來。

文件檢驗

另外,對於 4. 檢驗文件存在或者屬性的狀況,具體說來也分爲如下幾種:

  • -f /path/to 檢驗文件是否存在;!-f /path/to 檢驗文件是否不存在
  • -d /path/to/ 檢驗目錄是否存在;!-d /path/to/ 檢驗目錄是否不存在
  • -e /path/to/ 檢驗文件或者目錄或者連接是否存在;!-e /path/to/ 檢驗文件或者目錄或者連接是否不存在
  • -x /path/to 檢驗文件是否爲可執行文件;!-x /path/to 檢驗文件是否爲不可執行文件

if 指令舉例

if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /msie/$1 break;
}

location /xiao/ {
    if ($http_user_agent ~ Mozilla/5.0) { #若是是chrom瀏覽器
        rewrite ^(.*)$ http://www.example.com; #返回客戶端302
    } 
    if ($http_user_agent ~ curl) { # 若是是curl 發起請求
        rewrite ^/xiao/(.*)$ /xiao/$1.txt break; #獲得新的URI
    }  
} 

if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;  #提取了變量$1
}

if ($request_method = POST) {
    return 405; #能夠限制Request Method
}

if ($slow) {
    limit_rate 10k; #這個配置會加在location裏面
}

if ($invalid_referer) {
    return 403;
}

return

基本語法:return code [text];或者return code URL;或者return URL;
上下文:server, location, if

中止任何的進一步處理,而且將指定狀態碼返回給客戶端。若是狀態碼爲444(此狀態碼是非標準的),那麼直接關閉此TCP鏈接。

return的參數有四種形式:

set

基本語法:set $variable value;
上下文:server, location, if

這是一個有用的指令,用來定義變量,變量的值能夠包含字符串,另外的變量或者是兩者結合。

set $var = $http_x_forwarded_for;

注意:在Nginx中,除非特殊說明,大部分地方字符串的不須要引號括住,字符串和變量的拼接也不須要引號

rewrite_log

基本語法:rewrite_log on | off;
上下文:http, server, location, if

若是開啓 on,那麼當發生rewrite時,會產生一個notice級別的日誌;不然不會產生任何日誌。默認狀況下是不產生的,但在調試的時候能夠將其置爲on。

以上這些指令,基本涵蓋了rewrite模塊的全部應用,在須要改寫請求URI,或者作跳轉時很是有用。

相關文章
相關標籤/搜索