編寫本文時,使用的nginx版本為nginx/1.17.9和nginx/1.16.1
路由匹配規則
location路由匹配的大致規則:location [=|^~|~|~*|@] path { ... }
如果大家對這塊內容比較熟悉了,可以直接到優先級疑惑點這里看一個比較奇怪的匹配邏輯。
精確匹配(=)
location配置的path和客戶端請求的path完全一致時匹配成功。
匹配成功后,nginx就會停止搜索其他匹配項。
server {
listen 2020;
location = /test {
return 200 '=';
}
}
- 請求
localhost:2020/test
,匹配成功,響應內容為"=" - 請求
localhost:2020/test?num=1
,匹配成功,響應內容為"=" - 請求
localhost:2020/test/
,匹配失敗,響應狀態碼404 - 請求
localhost:2020/test/1
,匹配失敗,響應狀態碼404
前綴匹配(^~)
location配置的path為客戶端請求的path前綴時匹配成功。
匹配成功后,nginx還會判斷是否存在后面這種情況{(location修飾符為^~
|| location沒有修飾符) && location配置的path是客戶端請求的前綴},如果存在,就使用匹配項最多的作為最后的匹配結果。
server {
listen 2020;
location ^~ /test {
return 200 '^~';
}
}
- 請求
localhost:2020/test
,匹配成功,響應內容"^~" - 請求
localhost:2020/test/1
,匹配成功,響應內容"^~" - 請求
localhost:2020/test111
,匹配成功,響應內容"^~" - 請求
localhost:2020/tes
,匹配失敗,響應狀態碼404
server {
listen 2020;
location ^~ /test {
return 200 '/test';
}
location ^~ /test/1 {
return 200 '/test/1';
}
}
- 請求
localhost:2020/test
,匹配成功,響應內容"/test" - 請求
localhost:2020/tes
,匹配成功,響應內容"/test" - 請求
localhost:2020/test/1
,匹配成功,響應內容"/test/1"。這里兩個location配置都匹配上了,第一個location匹配項為1,第二個location匹配項為2,由於nginx選用匹配項最多的location,所以響應內容"/test/1"。
正則匹配(~ 和 ~*)
修飾符~
,正則匹配區分大小寫。修飾符~*
,正則匹配不區分大小寫。
正則匹配以location在文件中的定義順序從上到下進行匹配。匹配成功以后,nginx就停止搜索其他匹配項。
注意:mac os文件系統大小寫不敏感,因此nginx服務配置的location path不區分大小寫,nginx使用和效果是一樣的。linux文件系統大小寫敏感,因此nginx服務區分大小寫,nginx使用和效果與前面介紹的效果一致。
~例子
server {
listen 2020;
location ~ /test_a {
return 200 '~';
}
}
- 請求
localhost:2020/test_a
,匹配成功,響應內容"~" - 請求
localhost:2020/test_A
,匹配成功和失敗都有可能,得看nginx服務所在的系統對於大小寫是否敏感。mac os系統下,匹配成功,響應內容"~";linux系統下,匹配失敗,響應狀態碼404。
~*例子
server {
listen 2020;
location ~* /test_a {
return 200 '~*';
}
}
- 請求
localhost:2020/test_a
,匹配成功,響應內容"~*" - 請求
localhost:2020/test_A
,匹配成功,響應內容"~*"
優先級
注:優先級從上到下依次遞減。
- 精確匹配(=)
- 前綴匹配(^~)
- 正則匹配(~和~*)
- 通配符路徑(沒有任何修飾符,只有一個通配符路徑"/")
下面我們使用不同的location配置組合來匹配location:2020/test_a
這個請求。
server {
listen 2020;
location ^~ /test_a {
return 200 '^~';
}
location = /test_a {
return 200 '=';
}
}
- 請求
localhost:2020/test_a
,匹配成功,響應內容"=",精確匹配優先級比前綴匹配優先級大
server {
listen 2020;
location ~ /test_a {
return 200 '~';
}
location ^~ /test_a {
return 200 '^~';
}
}
- 請求
localhost:2020/test_a
,匹配成功,響應內容"^~",前綴匹配優先級比正則匹配優先級大
server {
listen 2020;
location / {
return 200 '/';
}
location ~ /test_a {
return 200 '~';
}
}
- 請求
localhost:2020/test_a
,匹配成功,響應內容"~",正則匹配優先級比通配符優先級大
優先級疑惑點
server {
listen 2020;
location ^~ /test {
return 200 '~';
}
location /test/a {
return 200 'null';
}
}
- 請求
localhost:2020/test/a
,匹配成功,響應內容"null",可以知道第二個location配置優先級比前綴優先級大,這個在前面前綴匹配有介紹。
我們將配置改成下面這個內容:
server {
listen 2020;
location /test {
return 200 'null';
}
location ^~ /test {
return 200 '~';
}
}
然后運行 nginx -t
來檢測配置文件是否正確,得到的結果是:nginx: [emerg] duplicate location "/test" in ...
,這里的意思是路徑為/test
的location重復了。看到這里,原本以為"^~"是nginx定義location時默認的修飾符,但是,實際可能並不是,我們看下面的例子。
server {
listen 2020;
location ~ /test {
return 200 '~';
}
location /test/a {
return 200 'null';
}
location ^~ /test {
return 200 '~';
}
}
- 請求
localhost:2020/test/a
,匹配成功,響應內容"~"(what?為什么返回的不是"null"),這里三個都匹配上了,但是nginx選用的是正則匹配結果,這個我不知道是什么原因,如果有大佬知道原因,還請大佬幫忙解惑。
location常用的參數
root & alias
兩個參數都是用來指定文件路徑。
注:之前很多文章都表示alias配置的路徑最后必須加上"/",這個到現在已經不適用了。我測試的時候,alias配置的路徑最后不添加"/",一樣可以正常使用。
最終指向的文件路徑區別
- root指向的文件實際路徑:root+location
- alias指向的文件實際路徑:alias
server {
listen 2020;
location /test {
root /data;
}
}
最終指向的文件路徑為/data/test
。
server {
listen 2020;
location /test {
alias /data;
}
}
最終指向的文件路徑為/data
;
使用上面的配置,我們發起請求localhost:2020/test/1.png
。
- root配置,該請求查找的文件路徑為
/data/test/1.png
- alias配置,該請求查找的文件路徑為
/data/1.png
定義位置區別
- root可以在http、server、location、if中定義內容
- alias只能在location中定義
root在http、server定義以后,location會默認繼承上層定義的內容,可以在location中使用root對上層root定義進行重寫,或者使用alias讓上層root在該lcation中失效。
正則匹配時定義區別
- root:按照前面說的方式使用
- alias:需要將正則匹配的內容添加到alias定義的路徑后面,具體的例子如下
server {
listen 2020;
location ~ /test {
alias /data;
}
}
請求localhost:2020/test/1.png
,匹配成功,但是沒有找到文件內容,響應404
server {
listen 2020;
location ~ /test(.*)$ {
alias /data$1;
}
}
請求localhost:2020/test/1.png
,匹配成功,能夠正常返回文件
proxy_pass
該參數用作反向代理,可以用來做負載均衡、前端解決跨域等功能。使用結構proxy_pass url
。
關於proxy_pass實現負載均衡,可以在nginx負載均衡中看到相關內容。
proxy_pass轉發請求,配置的url最后是否有"/",會呈現不同的效果。
server {
listen 2020;
location /api/ {
proxy_pass http://localhost:7001;
}
}
請求localhost:2020/api/component/list
,nginx會將該請求代理轉發到http://locahost:7001/api/component/list
。
應用場景:前端請求存在跨域,后端接口格式是api/業務路由
,前端請求的接口也是api/業務路由
。
server {
listen 2020;
location /api/ {
proxy_pass http://localhost:7001/;
}
}
請求localhost:2020/api/component/list
,nginx會將該請求代理轉發到http://locahost:7001/component/list
。
應用場景:后端接口格式是業務路由
,前端請求的接口是api/業務路由
,前端請求的接口前面加一個"api"是為了標識某個后端服務,后端接口中並沒用這個標識。
server {
listen 2020;
location /api/ {
proxy_pass http://localhost:7001/online;
}
}
請求localhost:2020/api/component/list
,nginx會將該請求代理轉發到http://locahost:7001/onlinecomponent/list
。
應用場景:沒遇到這樣的場景,一般都會用都會用"/"隔開路徑。
server {
listen 2020;
location /api/ {
proxy_pass http://localhost:7001/online/;
}
}
請求localhost:2020/api/component/list
,nginx會將該請求代理轉發到http://locahost:7001/online/component/list
。
rewrite
rewrite參數用來將客戶端請求重定向到一個新的地址。使用格式rewrite regex replacement [flag];
- regex(必填):正則匹配,只有正則匹配成功后,才能進行地址修改等后續步驟
- replacement(必填):新的url(以
http://
https://
$schema
等開頭)或者uri,正則匹配成功后會用這個值替換原來的請求地址。當replacement值是url時,客戶端請求發生重定向,因此會有兩次請求,第一次請求是客戶端原請求,響應的狀態碼為302,第二次請求的地址就是replacement值,本次rewrite邏輯運行完成以后,后續的rewrite不再匹配;當replacement值為uri時,客戶端請求可能發生重定向,是否發生重定向與flag參數有關 - flag(可選)
- break:本條rewrite邏輯運行完成以后,后續的rewrite不再匹配
- last:本條rewrite邏輯運行完成以后,后續的rewrite不再匹配,重新開始location路由匹配
- permanent:永久重定向,301
- redirect:臨時重定向,302
下面展示幾個例子:
server {
listen 2020;
location / {
rewrite /(.*) https://www.$1.com;
}
}
請求localhost:2020/baidu
,請求被重定向到https://www.baidu.com
。
server {
listen 2020;
location / {
rewrite (.*) https://www.baidu.com;
rewrite (.*) https://www.github.com;
}
}
請求localhost:2020
,請求被重定向到https://www.baidu.com
,查看network,里面有一條http://localhost:2020/
請求,響應的狀態碼為302,還以一條https://www.baidu.com/
請求。
server {
listen 2020;
location / {
rewrite (.*) /test redirect; # permanent也是可以的
}
location /test {
return 200 'ok';
}
}
請求localhost:2020
,請求被重定向到http://localhost:2020/test
,network中有兩條請求分別是響應狀態碼302(flag值為permanent時,狀態碼為301)的http://localhost:2020/
請求和響應狀態碼為200的http://localhost:2020/test
請求。
flag值為break或last都會終止后續rewrite匹配(不會終止proxy_pass等邏輯),兩者不同的點是,break不會發起一輪新的location路由匹配,而last會發起一輪新的location匹配。
server {
listen 2020;
location /last {
rewrite /last(.*) /test1;
rewrite /test1(.*) /test2 last;
rewrite /test2(.*) /test3;
}
location /break {
rewrite /break(.*) /test1;
rewrite /test1(.*) /test2 break;
rewrite /test2(.*) /test3;
}
location /test1 {
return 200 'test1';
}
location /test2 {
return 200 'test2';
}
location /test3 {
return 200 'test3';
}
}
- 請求
localhost:2020/last
,響應內容"test2"。這個請求被location /last {...}
匹配成功,因為rewrite /test1(.*) /test2 last
這里flag為last,所以這條rewrite邏輯運行完以后,就會忽略后續的rewrite,然后重新location路由匹配,重新匹配時請求變成http://localhost:2020/test2
,因此會被location /test2 {}
這條location匹配,所以響應內容為"test2"。 - 請求
localhost:2020/break
,響應狀態碼為404。這個請求被location /break {...}
匹配成功,因為rewrite /test1(.*) /test2 break
這里flag為break,所以這條rewrite邏輯運行完以后,就會忽略后續的rewrite,執行完當前location后還是沒有找到資源文件,因此返回狀態碼"404"。
將上面例子中location /break {...}
這部分內容修改一下,修改成如下內容:
location /break {
rewrite /break(.*) /test1;
rewrite /test1(.*) /test2 break;
rewrite /test2(.*) /test3;
proxy_pass http://localhost:2020;
}
- 請求
localhost:2020/break
,響應內容"test2",rewrite語句中flag值為break,不會影響proxy_pass語句,因此localhost:2020/break
實際會代理轉發到localhost:2020/test2
這個地址。
如果rewrite部分內容沒有看懂,可以到搞懂nginx的rewrite模塊查看更詳細的介紹。
index
index用於指定網站的起始頁面,默認值index index.html;
。
index參數只是用來指定文件的路徑,nginx根據index參數查找文件是否存在,如果存在就用文件路徑拼接成新的url,nginx內部重定向到這個新的url,來獲取到起始頁面資源。下面用具體的例子來進行說明(/data/test目錄下有一個index.html文件):
server {
listen 2020;
location / {
root /data/test;
index index.html;
}
}
請求localhost:2020
,響應內容為文件/data/test/index.html
內容。
下面對配置文件添加一些內容,用來匹配html文件請求:
server {
listen 2020;
location / {
root /data/test;
index index.html;
}
location ~ \.html$ {
return 200 'html文件請求攔截';
}
}
請求localhost:2020
,響應內容"html文件請求攔截"。這個例子很好的說明nginx內部會將初始頁文件路徑生成一個新的url,nginx內部重定向到這個新的url請求初始頁文件。
index后面可以跟多個文件路徑,當前一個文件不存在時,nginx會自動判斷后面文件是否存在。下面使用一個例子來展示(/data/test目錄下只有idnex.php文件):
server {
listen 2020;
location / {
root /data/test;
index index.html index.php;
}
}
請求localhost:2020
,nginx會首先判斷文件/data/test/index.html
是否存在,如果存在,就使用這個文件路徑來生成新的文件url,然后nginx內部重定向到這個文件資源;如果不存在,就判斷/data/test/index.php
文件是否存在,如果不存在就返回403,如果存在,就使用這個文件路徑來生成新的文件url,然后nginx內部重定向到這個文件資源。