天天看點

Nginx之rewrite實作URL重寫

作者:Java狂人

1.開篇

rewrite是nginx伺服器提供的一個重要功能,用于實作URL的重寫。例如我們通路https://aa.qq.com,打開的是https://age.qq.com/,這就是使用URL重寫的特性來實作的。

ngx_http_rewrite_module為實作URL重寫提供了指令支援。

官方文檔位址:https://nginx.org/en/docs/http/ngx_http_rewrite_module.html

接下來我們來看看rewrite的相關指令。

2.rewrite相關指令

2.1 set指令

作用域:server, location, if

文法:set $variable value;

該指令可以設定一個變量。

$variable:為變量的名稱,可以看到變量的名稱以$符号開頭,且不要與nginx預設的全局變量名相同。

value:為變量的值,可以是字元串、其他變量或者兩者的組合。

既然自定義的變量名不能與nginx的全局變量名相同,那就有必要了解使用rewrite功能時常用的nginx全局變量。

rewrite常用全局變量

全局變量 說明
$args 用于擷取請求中的參數。例如:http://192.168.110.92/test?name=tom&age=18,$args的值就為name=tom&age=18
$host 用于擷取請求中的主機部分的值。例如:http://192.168.110.92/test?name=tom&age=18,$host的值為192.168.110.92。
$http_user_agent 用于擷取請求中的User-Agent字段值
$remote_addr 用于擷取用戶端的IP位址
$remote_port 用于擷取用戶端與伺服器建立連接配接的端口号
$request_method 用于擷取用戶端的請求方式,例如GET、POST等
$request_uri 用于擷取目前請求的URI。例如:http://192.168.110.92/test?name=tom&age=18,$request_uri的值就為/test?name=tom&age=18
$query_string 與$args作用相同
$scheme 用于擷取用戶端請求使用的協定,例如http、https等
$server_addr 用于擷取服務端的IP位址
$server_name 用于擷取虛拟主機的名稱
$server_port 用于擷取虛拟主機的監聽的端口
$document_uri 用于擷取請求中的目前URI。例如:http://192.168.110.92/test?name=tom&age=18,$document_uri的值為/test
$uri 與$document_uri作用相同
$http_user_agent 用于擷取請求頭中User-Agent字段的值
$request_filename 目前請求的檔案路徑

下面我們設定自定義變量,順便體驗使用一下這些全局變量。

location /test {
     default_type text/html;
     set $username zhangsan;
     return 200 <html>><p>username:$username</p><p>request_uri:$request_uri</p><p>document_uri:$document_uri</p><p>uri:$uri</p><p>query_string:$query_string</p><p>args:$args</p></html>;
 }           

發起請求:http://192.168.110.98/test?name=tom

Nginx之rewrite實作URL重寫

2.2 if指令

作用域:server, location

文法:if (condition) { ... }

如果條件表達式為true,則執行該子產品大括号中的指令。

Tips:if和(之間有一個空格。

條件表達式有幾種形式:

1)變量名,如果變量的值為空字元串或"0",則為false,其他條件為true。

if ($variable){
 
 }           

2)使用"="和"!="比較變量和字元串是否相等,滿足條件則為true,否則為false。

if ($request_method = POST){
 
 }           

3)使用正規表達式與變量的值進行比對。變量與正規表達式之間使用~、~*、!~、!~*,如果正規表達式包含}或;,則整個表達式應該用單引号或雙引号括起來。

  • ~:表示比對正規表達式,區分大小寫
  • ~*:表示比對正規表達式,不區分大小寫
  • !~:表示比對正規表達式,區分大小寫,并對比對後的結果取反
  • !~*:表示比對正規表達式,不區分大小寫,并對比對後的結果取反
if ($http_user_agent ~ Mozilla/5.0){
 
 }           

4)判斷檔案是否存在:-f和!-f

if (-f $request_filename){
 
 }
 if (!-f $request_filename){
 
 }           

示例:

location /test {
     default_type text/html;
     if (!-f $request_filename){
         return 200 "<h1>file not exist</h1>";
     }
     root html;
 }           

我們已經在html目錄下準備了一個test.html。

通路http://192.168.110.98/test.html,可以正常顯示。

Nginx之rewrite實作URL重寫

通路http://192.168.110.98/test,因為檔案不存在,是以執行if條件塊的指令。

Nginx之rewrite實作URL重寫

5)判斷目錄是否存在:-d和!-d

6)判斷檔案、目錄或符号連結是否存在:-e和!-e

7)判斷檔案是否可以執行:-x和!-x

2.3 break指令

作用域:server, location, if;

文法:break;

在同一作用域中,中斷該指令之後的其他指令,位于其前面的指令配置生效,位于其後面的指令配置則無效。

示例:如果URL中存在參數,則執行if邏輯。

location /testBreak {
     default_type text/plain;
     set $username lisi;
     if ($args){
     set $username wangwu;
         break;
         set $username zhaoliu;
     }
     add_header username $username;
     return 200 $username;
 }           

通路http://192.168.110.98/testBreak,指令都正常執行。

Nginx之rewrite實作URL重寫

通路http://192.168.110.98/testBreak?name=zhangsan,說明執行了if邏輯。

Nginx之rewrite實作URL重寫

按break;語句的定義來說,在其執行後,其作用域外後面的指令應該正常執行才對,但是這裡直接傳回了404。這個時候,就需要我們檢視error.log。

Nginx之rewrite實作URL重寫

可以發現,錯誤提示為檔案未找到,根據錯誤提示,我們需要在html目錄下建立一個testBreak目錄,然後在testBreak目錄下建立一個index.html檔案

cd /usr/local/nginx
 mkdir testBreak
 vim index.html
 
 <html>
     <body>this is testBreak</body>
 </html>           

再次通路http://192.168.110.98/testBreak?name=zhangsan,可以看到break語句執行後,其作用域外後面的指令正常執行。

Nginx之rewrite實作URL重寫

2.4 return指令

作用域:server, location, if

文法:return code [text]; return code URL; return URL;

該指令可以停止處理并指定的響應碼傳回給前端。既可以傳回文本,也可以重定向URL。

示例:

location /testReturn {
	default_type text/plain;
	return 200 "test return";
}           
location /testReturn {
	return 302 https://www.baidu.com;
}           
location /testReturn {
	return https://www.baidu.com;
}           

2.5 rewrite指令

在了解set、if、break、return指令後,重頭戲rewrite指令登場。

作用域:server, location, if;

文法:rewrite regex replacement [flag];

regex:用來比對URI的正規表達式。

replacement:正則比對成功後,用來替換URI的字元串。如果該字元串以http://、https://或$scheme開頭,則處理将停止,并重定向URI到用戶端。

flag:是一個可選參數,其有4個候選值。

flag值 說明
last 停止處理rewrite指令,并使用重寫的URI去與各個location進行比對
break 停止處理rewrite指令,與break;效果一緻
redirect 如果replacement字元串不是以http://、https://或$scheme開頭,則重定向到重寫的URI,響應碼為302
permanent 重定向到重寫的URI,響應碼為301

rewrite指令通過正規表達式比對URI,并修改URI。可同時存在多個rewrite指令,按照順序依次對URI進行比對和處理。

示例:

location /rewrite {
	rewrite ^/rewrite/aaa\w+$ https://www.baidu.com;
	rewrite ^/rewrite/(bbb)\w+$ /$1 last;
	rewrite ^/rewrite/(ccc)\w+$ /$1 break;
	rewrite ^/rewrite/(ddd)\w+$ /$1 redirect;
	rewrite ^/rewrite/(eee)\w+$ /$1 permanent;
}

location /bbb {
	default_type text/plain;
	return 200 "this is bbb";
}

location /ccc {
	default_type text/plain;
	return 200 "this is ccc";
}

location /ddd {
	default_type text/plain;
	return 200 "this is ddd";
}

location /eee {
	default_type text/plain;
	return 200 "this is eee";
}           

2.6 rewrite_log指令

作用域:http, server, location, if

文法:rewrite_log on | off;

預設值:rewrite_log off;

該指令可以配置是否将ngx_http_rewrite_module指令的處理結果以notice級别的日志寫入到error_log中。

示例:

location /rewrite {
	# 開啟rewrite_log
	rewrite_log on;
	# 配置error_log
	error_log logs/error.log notice;
	rewrite ^/rewrite/aaa\w+$ https://www.baidu.com;
	rewrite ^/rewrite/(bbb)\w+$ /$1 last;
	rewrite ^/rewrite/(ccc)\w+$ /$1 break;
	rewrite ^/rewrite/(ddd)\w+$ /$1 redirect;
	rewrite ^/rewrite/(eee)\w+$ /$1 permanent;
}           

這樣我們就可以在error.log中看到notice級别的日志。

Nginx之rewrite實作URL重寫

3.使用場景

在熟悉了ngx_http_rewrite_module的相關指令後,我們來看看rewrite的相關使用場景。

3.1 域名重定向

場景:公司官網上線的時候位址為www.aaa.com,随着公司的不斷發展,需要将官網位址更新為www.bbb.com,但是需要在通路www.aaa.com能夠自動跳轉到www.bbb.com。

Tips:www.aaa.com和www.bbb.com需要指向同一IP。

解決方案:使用rewrite指令重寫URI。

server {
	listen 80;
	server_name www.aaa.com;
	rewrite ^(.*) https://www.bbb.com$1;
}           

3.2 優雅處理防盜鍊

前面我們在【Nginx靜态資源防盜鍊】一文中已經簡單的實作了靜态資源的防盜鍊,但是展示在頁面的是一個裂開的小圖檔,不夠美觀。

Nginx之rewrite實作URL重寫

我們可以如下配置:

location ~^/.*\.(png|jpg|gif|jfif) {
	valid_referers www.example.com;
	if ($invalid_referer){
		rewrite ^/ http://192.168.110.98/images/forbidden.png;
	}
	root   html;
}           

如果出現盜鍊的情況,将會出現類似于如下效果:

Nginx之rewrite實作URL重寫

以上就是Nginx之rewrite實作URL重寫,Nginx是多子產品化的,還有很多進階功能,我們後面繼續探索。

繼續閱讀