1. 程式人生 > >nginx location配置

nginx location配置

編寫本文時,使用的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,匹配成功,響應內容"~*"

優先順序

注:優先順序從上到下依次遞減。

  1. 精確匹配(=)
  2. 字首匹配(^~)
  3. 正則匹配(~和~*)
  4. 萬用字元路徑(沒有任何修飾符,只有一個萬用字元路徑"/")

下面我們使用不同的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內部重定向到這個檔案資源