快速掌握Nginx(二) —— Nginx的Location和Rewrite
1 location詳解
1.location匹配規則
Nginx中location的作用是根據Url來決定怎麼處理使用者請求(轉發請求給其他伺服器處理或者查詢本地檔案進行處理)。location支援正則表示式,配置十分靈活。我們可以在一個虛擬主機(nginx中的一個server節點)下配置多個location以滿足如動靜分離,防盜鏈等需求。
location語法是: location [=|~|~*|^~] /uri/ {… },具體解釋如下表:
符號 |
含義 |
location = /url |
= :開頭,表示精確匹配,uri必須完全一致才能匹配成功 |
location ^~ /Purl |
^~:Puri和請求url的開頭相同就匹配成功,且不再去匹配正則,也屬於普通匹配 |
location /Purl |
普通匹配,Purl和使用者請求url的開頭相同就匹配成功,如果有多個普通匹配都匹配成功則按最長的 。 如有location /static/,和oaction /static/img/ 當請求是www.mysite.com/static/img/1.jpg時,第二個location匹配的更長,所以和第二個loaction匹配成功。 |
location ~ reg |
~ :區分大小寫的正則匹配 |
location ~* reg |
~* :不區分大小寫的正則匹配 |
location的匹配順序是: = /url > ^~ /Purl > /Purl > ~ 和 ~* ,具體流程如下圖所示,需要注意:一般情況下,匹配成功了普通字串location後還會進行正則表示式location匹配。兩種情況除外:① 使用“=” ,即精準匹配,如果匹配成功就立即停止其他匹配;② 使用“^~”字首 ,這個字首告訴nginx ,如果匹配成功不再進行正則匹配。
簡單總結:
1. 先進行精準匹配,如果匹配成功,立即返回結果並結束匹配過程。
2. 進行普通匹配,如果有多個location匹配成功,將“最長字首”的location作為臨時結果(如果是 ^~型別的普通匹配成功則直接返回結果,結束匹配過程)。
3. 由上至下逐一進行正則匹配,一旦匹配成功1個,立即返回結果,並結束解析過程;如果沒有一個正則匹配成功,那麼將普通匹配的最長字首location作為最終結果返回,並結束匹配過程。
2. 實際使用建議
實際使用中,個人覺得每個虛擬主機下(server節點下)至少有三個匹配規則定義,如下: #直接匹配網站根,通過域名訪問網站首頁比較頻繁,使用這個會加速處理,官網如是說。 #這裡是直接轉發給後端應用伺服器了,也可以是一個靜態首頁 # 第一個必選規則 location = / { proxy_pass http://tomcat:8080/index } # 第二個必選規則是處理靜態檔案請求,這是nginx作為http伺服器的強項 # 有兩種配置模式,目錄匹配或字尾匹配,任選其一或搭配使用 location ^~ /static/ { root /webroot/static/; } location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ { root /webroot/res/; } #第三個規則就是通用規則,用來轉發動態請求到後端應用伺服器 #非靜態檔案請求就預設是動態請求,自己根據實際把握 #畢竟目前的一些框架的流行,帶.php,.jsp字尾的情況很少了 location / { proxy_pass http://tomcat:8080/ }
實際使用建議參考自: https://segmentfault.com/a/1190000002797606
2 rewrite詳解
1 rewrite簡單認識
rewrite模組即ngx_http_rewrite_module模組,主要功能是 實現URI重定向 。rewrite模組會通過正則匹配重寫URI,然後內部跳轉再匹配location,或者直接做30x重定向返回客戶端。Nginx的rewrite功能需要PCRE的支援,PCRE是perl相容正則表示式庫。rewrite指令的語法十分簡單如下:
rewrite將符合正則的內容替換為新的替代內容 rewrite<regex><replacement>[flag]; 關鍵字正則替代內容flag標記 正則: perl相容正則表示式語句進行規則匹配 替代內容: 將正則匹配的內容替換成replacement flag標記: rewrite支援的flag標記 ------------------------------------------------------------------------------- flag標記說明: last#匹配完成後不再匹配當前環境下的其他rewrite指令,開始匹配新的location URI規則 break#匹配完成即終止,不再匹配後面的任何規則 redirect#返回302臨時重定向,瀏覽器地址會顯示跳轉後的URL地址 permanent#返回301永久重定向,瀏覽器位址列會顯示跳轉後的URL地址
使用rewrite時也會用到,幾個常用的指令彙總如下:
指令 |
使用範圍 |
作用 |
if ( condition ){ // 符合條件執行} |
location,server |
條件判斷。 = != 判斷是否相等 ~ ~* 判斷是否符合正則 -e !-e 判斷檔案,目錄,符號連結是否存在 -d !-d 判斷目錄是否存在 -f !-f 判斷檔案是否存在 -x !-x 判斷是否可執行 |
break |
server,location,if |
不再繼續執行任何指令,直接退出規則的執行 |
return |
server,location,if |
結束規則的執行和返回狀態碼給客戶端;如 return 403; |
set variable ‘value’ |
http,server,location,if |
新建變數,並賦值 ;如 set varx 'hello' |
一個簡單的栗子,簡單瞭解下rewrite:
server{ listen 80; server_name www.mysite.com; #在server中調轉到 愛奇藝 #rewrite ^/(.*) https://www.iqiyi.com break; location = /{ #location中跳轉到百度 rewrite ^/(.*) http://www.baidu.com; root html; index index.html; } #日誌記錄 error_log logs/mysite.error.log error; access_loglogs/mysite.access.logmain; }
我們知道預設情況訪問nginx的虛擬主機會展示nginx的歡迎介面,我們通過rewrite指令跳轉到百度。輸入虛擬機器的IP,訪問結果不再是nginx歡迎頁,而是302跳轉到百度如下:
2 rewrite的執行過程
nginx中我們可以有多個rewrite指令,預設情況下rewrite從上到下依次執行,並按最後一個匹配成功的作為最終結果。一種特殊情況是當replacement中包含http/https等協議名時,直接302跳轉到replacement指定的url,不再執行後續的rewrite指令。
那麼如果我們想在執行一條rewrite指令後不再執行後續指令怎麼辦呢?這時就可以用rewrite中的flag標記,四種flag標記都可以實現不再往下執行其他rewrite指令的作用,但是每種flag標記的使用場景不同。介紹語法的時候已經介紹了四種flag的作用,我們來看一個栗子吧:
server{ listen 80; server_name www.mysite.com; location = /{ #跳轉到百度 rewrite ^/(.*) http://www.baidu.com; #跳轉到/test1 rewrite ^/(.*) /test1; #跳轉到/test2 rewrite ^/(.*) /test2; root html; index index.html; } location /test1{ return 401; } location /test2{ return 402; } #日誌記錄 error_log logs/mysite.error.log error; access_loglogs/mysite.access.logmain; }
server進行上邊的配置時,我們訪問虛擬機器IP 192.168.70.132,會跳轉到百度頁面,因為replacement包含了http協議名,不在執行後續的rewrite指令;
如果把第一個rewrite註釋掉,會調整到402錯誤頁,因為rewrite的最終結果時以最後一個匹配成功的為準,最後匹配到 rewite /test2指令,然後找到location /test2返回402錯誤碼;
如果我們在rewrite ^?(.*) /test1後邊加上last標記 ,表示不再匹配後邊的rewrite,會跳到401錯誤頁,url不變還是http://192.168.70.132;
如果我們在rewrite ^?(.*) /test1後邊加上redirect 或者 permanent 標記 ,表示不再匹配後邊的rewrite,會跳轉到401錯誤頁(redirect的跳轉碼為302,permanet的跳轉碼時301),url會改變成 http://192.168.70.132/test1;
如果我們在rewrite ^?(.*) /test1後邊加上break標記 ,表示不再匹配任何規則,會跳轉到404錯誤頁;因為break標記不會再執行任何規則,所以不會再去找location test1,而是直接找 html/test1資源,所以出現404錯誤。
3 一些常用的全域性變數
在使用rewrite指令時我們經常會用到一些常用的全域性變數,這些全域性變數定義在nginx/conf/fastcgi.conf中,列舉如下:
變數 |
含義 |
$args |
請求中的引數,同$query_string |
$content length |
請求頭中的Content-length欄位。 |
$content_type |
請求頭中的Content-Type欄位。 |
$document_root |
當前請求在root指令中指定的值。 |
$host |
請求主機頭欄位,否則為伺服器名稱。 |
$http_user_agent |
使用者代理,一般為使用者瀏覽器資訊 |
$http_cookie |
客戶端cookie資訊 |
$limit_rate |
這個變數可以限制連線速率。 |
$request_method |
客戶端請求的動作,通常為GET或POST。 |
$remote_addr |
客戶端的IP地址。 |
$remote_port |
客戶端的埠。 |
$remote_user |
已經經過Auth Basic Module驗證的使用者名稱。 |
$request_filename |
當前請求的檔案路徑,由root或alias指令與URI請求生成。 |
$scheme |
協議名(如http,https)。 |
$server_protocol |
請求使用的協議,通常是HTTP/1.0或HTTP/1.1。 |
$server_addr |
伺服器地址,在完成一次系統呼叫後可以確定這個值。 |
$server_name |
伺服器名稱。 |
$server_port |
請求到達伺服器的埠號。 |
$request_uri |
包含請求引數的原始URI,不包含主機名,如”/user/getuser?id=100”。 |
$uri |
不帶請求引數的當前URI,$uri不包含主機名,如”/user/getuser”。 |
$document_uri |
與$uri相同。 |
這裡列舉幾個rewrite的簡單栗子來幫助理解:
① 禁止特定IP訪問
server{ listen 80; server_name localhost; location /{ #如果客戶端IP是192.168.70.1,那麼拒接響應 if ($remote_addr = 192.168.70.1){ return 403; } root html; index index.html; } }
通過IP為192.168.70.1的電腦去訪問時,結果如下:
② 根據瀏覽器不同跳轉到不同頁面
#如果是google訪問的,重定向到 html/chrome.html頁面 location /{ if ($http_user_agent ~ Chrome){ rewrite ^.*$/chrome.html; break; } root html; index index.html; }
③ 檔案不存在返回404,寫的比較繁瑣,主要是演示rewrite的用法
server{ listen 80; server_name www.mysite.com; location /{ #如果檔案不存在,跳轉到notfound, if (!-f/usr/local/nginx/html/aaa.html){ rewrite ^/(.*) /notfound ; } root html; index index.html; } location ~ /notfound { return 404; } error_log logs/mysite.error.log error; access_loglogs/mysite.access.logmain; }
小結:loaction和rewrite是nginx中最核心的指令,通過location和rewrite我們可以實現動靜分離/規範客戶端url等功能,因為支援perl的正則表示式,用法十分靈活。這裡簡單做了一些總結,如果有不正確的地方請指出。
參考文章:
【1】https://www.cnblogs.com/coder-yoyo/p/6346595.html
【2】https://www.cnblogs.com/czlun/articles/7010604.html
【3】https://www.cnblogs.com/crazylqy/p/6892010.html