2021年3月16日 星期二

Nginx 設定裡的 location 匹配選擇機制

最近在設定 Nginx 時,才發現原來它可以根據不同請求的 URI 來給予不同的配置規則,如導向不同的應用程式(有點後知後覺XD),而在實際用過後,登登登,不得了一試成主顧。這讓我想到以前在 IIS 上都沒做過類似的事情,還自以為很懂 IIS,完全自我感覺良好無誤...,既然現在已經知道可以這麼做並且自己也將學習重心移轉到 Nginx 上,當然就要把這次的學習過程給記錄下來,所以這篇就將自己對 location 的認知做個整裡。


這邊先以下面的配置為開端

server {
    ... other configurations ...

    # 網域用 www.domain.com 當例子 
    server_name  www.domain.com 

    ... other configurations ...

    # 最基礎的配置,請看之後的說明
    location / {
        ....
    }  
}


一般配置(最長路徑),會搜尋全部的 location 後才挑選出符合最長路徑的配置檔(所以有可能被其它規則攔截)

server {
    ...
    server_name  www.domain.com 
    ...

    # 最基本的配置,會匹配到所有 http://www.domain.com 的請求,但是優先順序會被排到正規表示法和最長路徑之後
    # 例如 http://www.domain.com/abc 或 http://www.domain.com/xyz/dunk 兩者皆可符合該規則 
    location / {
        ....
    }

    # 匹配到以 /news 開頭的所有請求,但還不會馬上採用,會繼續往下搜尋是否有較長符合路徑的項目
    location /news {
        ....
    }

    # 匹配到以 /news/taiwan 開頭的所有請求,也是不會馬上採用,但若是有來自 http://www.domain.com/news/taiwan/... 開頭的請求,則會取代上面 /news 配置成為優先保留項再繼續往下搜尋
    location /news/taiwan {
        ....
    }

    # 匹配到以 /news/taiwan/keelung 開頭的所有請求,也是不會馬上採用,分為以下兩個情境
    # 1. 來自 http://www.domain.com/news/taiwan/hsinchu 開頭的請求,依然會採用上面 /news/taiwan 的配置然後繼續往下搜尋
    # 2. 來自 http://www.domain.com/news/taiwan/keelung 開頭的請求,則會取代上面 /news/taiwan 的配置成為新的優先保留項然後才再繼續往下搜尋
    location /news/taiwan/keelung {
        ....
    }
}


使用=代表精準匹配,需完全符合且匹配到會立即採用

server {
    ... other ...

    server_name  www.domain.com 

    ... other ...

    # 只能 http://www.domain.com 才能符合,後面不能帶任何字元,e.g. http://www.domain.com (O), http://www.domain.com/abc (X)
    location = / {
        ....
    }

    # 來自 http://www.domain.com/news/taiwan/taipei 的要求會立即採用此配置
    location = /news/taiwan/taipei {
        ....
    } 
}


使用~代表使用正規表示法來做匹配,會區分大小寫,若要不區分大小寫需使用~*,由於第一個匹配到規則的配置會立即採用,所以採用此種方式的配置其順序很重要

server {
    ... other ...

    server_name  www.domain.com 

    ... other ...

    # 1. 來自 http://www.domain.com/images 開頭的要求會匹配此規則且立即採用該配置,但 http://www.domain.com/Images 開頭的要求則不符合該規則
    location ~ ^/images {
        ....
    }

    # 來自 http://www.domain.com/images 和 http://www.domain.com/Images 開頭的要求皆能符合此規則且會立即採用該配置
    location ~* ^/images {
        ....
    }

    # 匹配所有以 .gif 和 .jpg 和 .jpeg 結尾的要求,這邊分兩個情境來說明
    # 1. 來自 http://www.domain.com/Images/dog.jpg 的請求雖然符合該配置的規則,但是卻會先被上面的 ~* ^/images 這條規則先攔截,因此該請求其實是到不了這條規則的
    # 2. 來自 http://www.domain.com/news/images/cat.gif 的請求符合該配置的規則且沒在其它地方被攔截,所以會立即採用該配置
    location ~* \.(gif|jpg|jpeg)$ {
        ....
    }

    # 匹配所有 URI 路徑以 admin 和 swagger 和 api 開頭的要求,這邊分兩個情境來說明
    # 例如來自 http://www.domain.com/admin/... 或 http://www.domain.com/Admin/... 開頭的請求皆符合此規則且會立即採用該配置
    location ~* ^/(admin|swagger|api)/? {
        ....
    }  
}


使用^~代表排除正規表示法的匹配,類似於一般配置,不同的是一旦符合即馬上採用該配置

server {
    ...
    server_name  www.domain.com 


    # 匹配到所有請求,但還不會馬上採用,會繼續往下搜尋是否有其它較長符合路徑的項目
    location / {
        ....
    }

    # 匹配到以 /news 開頭的所有請求,但還不會馬上採用,會繼續往下搜尋是否有較長符合路徑的項目
    location /news {
        ....
    }

    # 匹配到以 /news/taiwan 開頭的所有請求,因為使用了 ^~ 所以如果有符合 /news/taiwan 開頭的 URI 會立即採用該配置,停止往下搜尋
    # 1. 來自 http://www.domain.com/news/taiwan/keelung/index 的請求符合該配置的規則,所以採用該配置
    # 2. 來自 http://www.domain.com/news/japan/osaka/index 的請求不符合該配置的規則,繼續往下搜尋
    location ^~ /news/taiwan {
        ....
    }

    # 匹配到以 /news/japan 開頭的所有請求,但還不會馬上採用,會繼續往下搜尋是否有較長符合路徑的項目
    location /news/japan {
        ....
    }
}


最後就用以下這張表來作個總結

參數說明立即採用
最長路徑No
=精準匹配Yes
^~效果類似最長路徑,但會馬上採用Yes
~正規表示法,區分大小寫Yes
~*正規表示法,不區分大小寫Yes




參考資料

[大軒軒的筆記本] Nginx server 和 location 的優先順序

[清白之年] Nginx 基礎配置項介紹

訪客統計

103171