搞懂nginx的rewrite模塊

以前在配置nginx時,老是遇到rewrite指令的last和break標識的問題,看到的資料大都是last 基本上都用這個 Flag,break 停止 Rewirte,不在繼續匹配。看完以後仍是有點懵,後來看了下rewrite模塊的文檔,終於搞懂了,這個模塊內容也不是太多,索性整個把這個模塊都好好整理下吧html

ngx_http_rewrite_module 模塊用來使用正則表達式(PCRE)改變請求的URI,返回重定向,並有條件地選擇配置。nginx

指令執行順序

  1. 首先順序執行server塊中的rewrite模塊指令,獲得rewrite後的請求URI
  2. 而後循環執行以下指令正則表達式

    > 若是沒有遇到中斷循環標誌,此循環最多執行10次,可是咱們可使用break指令來中斷rewrite後的新一輪的循環

(1). 依據rewrite後的請求URI,匹配定義的 location 塊瀏覽器

(2). 順序執行匹配到的 location 中的rewrite模塊指令服務器

指令

break

Context: server, location, ifcurl

中止執行 ngx_http_rewrite_module 的指令集,可是其餘模塊指令是不受影響的
例子說明url

server {
    listen 8080;
    # 此處 break 會中止執行 server 塊的 return 指令(return 指令屬於rewrite模塊)
    # 若是把它註釋掉 則全部請求進來都返回 ok
    break;
    return 200 "ok";
    location = /testbreak {
        break;
        return 200 $request_uri;
        proxy_pass http://127.0.0.1:8080/other;
    }
    location / {
        return 200 $request_uri;
    }
}

# 發送請求以下
# curl 127.0.0.1:8080/testbreak
# /other

# 能夠看到 返回 `/other` 而不是 `/testbreak`,說明 `proxy_pass` 指令仍是被執行了
# 也就是說 其餘模塊的指令是不會被 break 中斷執行的
# (proxy_pass是ngx_http_proxy_module的指令)

if

Context: server, location代理

依據指定的條件決定是否執行 if 塊語句中的內容日誌

if 中的幾種 判斷條件

  1. 一個變量名,若是變量 $variable 的值爲空字符串或者字符串"0",則爲false
  2. 變量與一個字符串的比較 相等爲(=) 不相等爲(!=) 注意此處不要把相等當作賦值語句啊
  3. 變量與一個正則表達式的模式匹配 操做符能夠是(~ 區分大小寫的正則匹配, ~*不區分大小寫的正則匹配, !~ !~*,前面二者的非)
  4. 檢測文件是否存在 使用 -f(存在) 和 !-f(不存在)
  5. 檢測路徑是否存在 使用 -d(存在) 和 !-d(不存在) 後面判斷能夠是字符串也但是變量
  6. 檢測文件、路徑、或者連接文件是否存在 使用 -e(存在) 和 !-e(不存在) 後面判斷能夠是字符串也但是變量
  7. 檢測文件是否爲可執行文件 使用 -x(可執行) 和 !-x(不可執行) 後面判斷能夠是字符串也但是變量

注意 上面 第1,2,3條被判斷的必須是 變量, 4, 5, 6, 7則能夠是變量也但是字符串code

set $variable "0"; 
if ($variable) {
    # 不會執行,由於 "0" 爲 false
    break;            
}

# 使用變量與正則表達式匹配 沒有問題
if ( $http_host ~ "^star\.igrow\.cn$" ) {
    break;            
}

# 字符串與正則表達式匹配 報錯
if ( "star" ~ "^star\.igrow\.cn$" ) {
    break;            
}
# 檢查文件類的 字符串與變量都可
if ( !-f "/data.log" ) {
    break;            
}

if ( !-f $filename ) {
    break;            
}

return

Context: server, location, if

return code [text];
return code URL;
return URL;

中止處理並將指定的code碼返回給客戶端。 非標準code碼 444 關閉鏈接而不發送響應報頭。

0.8.42版本開始, return 語句能夠指定重定向 url (狀態碼能夠爲以下幾種 301,302,303,307),
也能夠爲其餘狀態碼指定響應的文本內容,而且重定向的url和響應的文本能夠包含變量

有一種特殊狀況,就是重定向的url能夠指定爲此服務器本地的urI,這樣的話,nginx會依據請求的協議$schemeserver_name_in_redirectport_in_redirect自動生成完整的 url (此處要說明的是server_name_in_redirectport_in_redirect 指令是表示是否將server塊中的 server_namelisten 的端口 做爲redirect用 )

# return code [text]; 返回 ok 給客戶端
location = /ok {
    return 200 "ok";
}

# return code URL; 臨時重定向到 百度
location = /redirect {
    return 302 http://www.baidu.com;
}

# return URL; 和上面同樣 默認也是臨時重定向
location = /redirect {
    return http://www.baidu.com;
}

rewrite

Context: server, location, if

rewrite regex replacement [flag];

rewrite 指令是使用指定的正則表達式regex來匹配請求的urI,若是匹配成功,則使用replacement更改URIrewrite指令按照它們在配置文件中出現的順序執行。可使用flag標誌來終止指令的進一步處理。若是替換字符串replacementhttp://https://$ scheme開頭,則中止處理後續內容,並直接重定向返回給客戶端。

第一種狀況 重寫的字符串 帶http://

location / {
    # 當匹配 正則表達式 /test1/(.*)時 請求將被臨時重定向到 http://www.$1.com
    # 至關於 flag 寫爲 redirect
    rewrite /test1/(.*) http://www.$1.com;
    return 200 "ok";
}
# 在瀏覽器中輸入 127.0.0.1:8080/test1/baidu 
# 則臨時重定向到 www.baidu.com
# 後面的 return 指令將沒有機會執行了

第二種狀況 重寫的字符串 不帶http://

location / {
    rewrite /test1/(.*) www.$1.com;
    return 200 "ok";
}
# 發送請求以下
# curl 127.0.0.1:8080/test1/baidu
# ok

# 此處沒有帶http:// 因此只是簡單的重寫。請求的 uri 由 /test1/baidu 重寫爲 www.baidu.com
# 由於會順序執行 rewrite 指令 因此 下一步執行 return 指令 響應了 ok

rewrite 的四個 flag

  1. last
    中止處理當前的ngx_http_rewrite_module的指令集,並開始搜索與更改後的URI相匹配的location;
  2. break
    中止處理當前的ngx_http_rewrite_module指令集,就像上面說的break指令同樣;
  3. redirect
    返回302臨時重定向。
  4. permanent
    返回301永久重定向。
# 沒有rewrite 後面沒有任何 flag 時就順序執行 
# 當 location 中沒有 rewrite 模塊指令可被執行時 就重寫發起新一輪location匹配
location / {
    # 順序執行以下兩條rewrite指令 
    rewrite ^/test1 /test2;
    rewrite ^/test2 /test3;  # 此處發起新一輪location匹配 uri爲/test3
}

location = /test2 {
    return 200 "/test2";
}  

location = /test3 {
    return 200 "/test3";
}
# 發送以下請求
# curl 127.0.0.1:8080/test1
# /test3
last 與 break 的區別

last 和 break同樣 它們都會終止此 location 中其餘它rewrite模塊指令的執行,
可是 last 當即發起新一輪的 location 匹配 而 break 則不會

location / {
    rewrite ^/test1 /test2;
    rewrite ^/test2 /test3 last;  # 此處發起新一輪location匹配 uri爲/test3
    rewrite ^/test3 /test4;
    proxy_pass http://www.baidu.com;
}

location = /test2 {
    return 200 "/test2";
}  

location = /test3 {
    return 200 "/test3";
}
location = /test4 {
    return 200 "/test4";
}
# 發送以下請求
# curl 127.0.0.1:8080/test1
# /test3 

當若是將上面的 location / 改爲以下代碼
location / {
    rewrite ^/test1 /test2;
    # 此處 不會 發起新一輪location匹配;當是會終止執行後續rewrite模塊指令 重寫後的uri爲 /more/index.html
    rewrite ^/test2 /more/index.html break;  
    rewrite /more/index\.html /test4; # 這條指令會被忽略

    # 由於 proxy_pass 不是rewrite模塊的指令 因此它不會被 break終止
    proxy_pass https://www.baidu.com;
}
# 發送以下請求
# 瀏覽器輸入 127.0.0.1:8080/test1 
# 代理到 百度產品大全頁面 https://www.baidu.com/more/index.html;
友情提醒下

此處提一下 在上面的代碼中即便將 proxy_pass 放在 帶有 breakrewrite上面它也是會執行的,這就要扯到nginx的執行流程了。你們有興趣能夠了解下。

rewrite 後的請求參數

若是替換字符串replacement包含新的請求參數,則在它們以後附加先前的請求參數。若是你不想要以前的參數,則在替換字符串 replacement 的末尾放置一個問號,避免附加它們。

# 因爲最後加了個 ?,原來的請求參數將不會被追加到rewrite以後的url後面 
rewrite ^/users/(.*)$ /show?user=$1? last;

rewrite_log

Context: http, server, location, if

開啓或者關閉 rewrite模塊指令執行的日誌,若是開啓,則重寫將記錄下notice 等級的日誌到nginxerror_log 中,默認爲關閉 off

Syntax:    rewrite_log on | off;

set

Context: server, location, if

設置指定變量的值。變量的值能夠包含文本,變量或者是它們的組合形式。

location / {
    set $var1 "host is ";
    set $var2 $host;
    set $var3 " uri is $request_uri";
    return 200 "response ok $var1$var2$var3";
}
# 發送以下請求
# curl 127.0.0.1:8080/test
# response ok host is 127.0.0.1 uri is /test

uninitialized_variable_warn

Context: http, server, location, if

控制是否記錄 有關未初始化變量的警告。默認開啓

相關文章
相關標籤/搜索