Nginx的location配置規則梳理

散盡浮華發表於2017-05-03

 

Nginx幾乎是當下絕大多數公司在用的web應用服務,熟悉Nginx的配置,對於我們日常的運維工作是至關重要的,下面就Nginx的location配置進行梳理:

1)location匹配的是nginx的哪個變數?

$request_uri

2)location的匹配種類有哪些?

格式:location [ 空格 | = | ~ | ~* | !~ | !~* | @ ] /uri/ {}
解釋:
=   表示精確匹配,如果找到,立即停止搜尋並立即處理此請求。
~   表示執行一個正則匹配,區分大小寫匹配
~*  表示執行一個正則匹配,不區分大小寫匹配
!~  區分大小寫不匹配
!~* 不區分大小寫不匹配
^~  即表示只匹配普通字元(空格)。使用字首匹配,^表示“非”,即不查詢正規表示式。如果匹配成功,則不再匹配其他location。
@   指定一個命名的location,一般只用於內部重定向請求。例如 error_page, try_files
/   表示通用匹配,任何請求都會匹配到

------------------------------------------------------------------------
對應示例說明:
1)=
server {
  server_name wangshibo.com;
  location = /abcd {
  […]
  }
}
匹配情況:
    http://wangshibo.com/abcd        # 正好完全匹配
    http://wangshibo.com/ABCD        # 如果執行 Nginx server 的系統本身對大小寫不敏感,比如 Windows ,那麼也匹配
    http://wangshibo.com/abcd?param1?m2    # 忽略查詢串引數(query string arguments),這裡就是 /abcd 後面的 ?param1?m2
    http://wangshibo.com/abcd/    # 不匹配,因為末尾存在反斜槓(trailing slash),Nginx 不認為這種情況是完全匹配
    http://wangshibo.com/abcde    # 不匹配,因為不是完全匹配

2)(None)
可以不寫 location modifier ,Nginx 仍然能去匹配 pattern 。這種情況下,匹配那些以指定的 patern 開頭的 URI,注意這裡的 URI 只能是普通字串,不能使用正規表示式。
server {
  server_name website.com;
  location /abcd {
  […]
  }
}
匹配情況:
    http://wangshibo.com/abcd        # 正好完全匹配
    http://wangshibo.com/ABCD        # 如果執行 Nginx server 的系統本身對大小寫不敏感,比如 Windows ,那麼也匹配
    http://wangshibo.com/abcd?param1?m2    # 忽略查詢串引數(query string arguments),這裡就是 /abcd 後面的 ?param1?m2
    http://wangshibo.com/abcd/    # 末尾存在反斜槓(trailing slash)也屬於匹配範圍內
    http://wangshibo.com/abcde    # 仍然匹配,因為 URI 是以 pattern 開頭的

3)~
這個 location modifier 對大小寫敏感,且 pattern 須是正規表示式
server {
  server_name wangshibo.com;
  location ~ ^/abcd$ {
  […]
  }
}
匹配情況:
    http://wangshibo.com/abcd        # 完全匹配
    http://wangshibo.com/ABCD        # 不匹配,~ 對大小寫是敏感的
    http://wangshibo.com/abcd?param1?m2    # 忽略查詢串引數(query string arguments),這裡就是 /abcd 後面的 ?param1?m2
    http://wangshibo.com/abcd/    # 不匹配,因為末尾存在反斜槓(trailing slash),並不匹配正規表示式 ^/abcd$
    http://wangshibo.com/abcde    # 不匹配正規表示式 ^/abcd$
注意:對於一些對大小寫不敏感的系統,比如 Windows ,~ 和 ~* 都是不起作用的,這主要是作業系統的原因。

4)~*
與 ~ 類似,但這個 location modifier 不區分大小寫,pattern 須是正規表示式
server {
  server_name website.com;
  location ~* ^/abcd$ {
  […]
  }
}
匹配情況:
    http://wangshibo.com/abcd        # 完全匹配
    http://wangshibo.com/ABCD        # 匹配,這就是它不區分大小寫的特性
    http://wangshibo.com/abcd?param1?m2    # 忽略查詢串引數(query string arguments),這裡就是 /abcd 後面的 ?param1?m2
    http://wangshibo.com/abcd/    # 不匹配,因為末尾存在反斜槓(trailing slash),並不匹配正規表示式 ^/abcd$
    http://wangshibo.com/abcde    # 不匹配正規表示式 ^/abcd$

5)^~
匹配情況類似 2. (None) 的情況,以指定匹配模式開頭的 URI 被匹配,不同的是,一旦匹配成功,那麼 Nginx 就停止去尋找其他的 Location 塊進行匹配了(與 Location 匹配順序有關)

6. @
用於定義一個 Location塊,且該塊不能被外部Client 所訪問,只能被Nginx內部配置指令所訪問,比如try_files 或 error_page
	location @resize {
            rewrite ^/(.*)/cache/(.*)?(.*)$ /resize.php?dir=$1&path=$2$3;
            rewrite ^/(.*)/orgi/cert/(.*)?(.*)$ /pass/resize?dir=$1&type=cert&path=$2$3&is_orgi=true;
            rewrite ^/(.*)/orgi/card/(.*)?(.*)$ /pass/resize?dir=$1&type=card&path=$2$3&is_orgi=true;
            rewrite ^/(.*)/orgi/(.*)/(.*)?(.*)$ /pass/resize?dir=$1&type=$2&path=$3$4&is_orgi=true;
            include fastcgi_params;
        }

如下示例:

#通用匹配
location / {
	  root /var/www/web/;
	  autoindex on;
	  autoindex_exact_size off;
	  autoindex_localtime on;
	  access_log /var/www/log/nginx/access.log;
	  error_log /var/www/log/nginx/error.log;
}

#正則匹配
#proxy the php scripts to php-fpm
location ~ \.php(.*)$  {
        root /var/www/web/;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_read_timeout 300;
        }

#精確匹配
location = /hello.php {
		root /var/www/web/;
		rewrite ^(.*)$ http://www.wangshibo.com  redirect;
}


產生的效果如下:
訪問根目錄/,匹配到location /
訪問除hello.php之外的其它php程式,匹配到location ~ \.php$,並且用php5-fpm去執行
訪問hello.php,匹配到location = /hello.php,訪問被重定向到http://www.wangshibo.com

3)location搜尋優先順序順序如何?

精確匹配 > 字串匹配( 長 > 短 [ 注: ^~ 匹配則停止匹配 ]) > 正則匹配( 上 > 下 )
 
在nginx的location和配置中location的順序沒有太大關係。正location表示式的型別有關。相同型別的表示式,字串長的會優先匹配。
 
優先順序排列:
1)等號型別(=)的精確匹配優先順序最高,精確匹配只能命中一個。一旦匹配成功,則不再查詢其他匹配項。
2)^~型別表示式,即字串匹配,使用匹配最長的最為匹配結果。一旦匹配成功,則不再查詢其他匹配項。
3)正規表示式型別(~ ~*)的優先順序次之。如果有多個location的正則能匹配的話,則使用正規表示式最長的那個。
4)常規字串匹配型別。按字首匹配。
 
特別注意:
字串匹配優先搜尋,但是隻是記錄下最長的匹配 (如果 ^~ 是最長的匹配,則會直接命中,停止搜尋正則),然後繼續搜尋正則匹配,如果有正則匹配,則命中正則匹配,如果沒有正則匹配,則命中最長的字串匹配.
 
例項說明:
1)先來測試下區分大小寫和不區分大小寫的優先順序.如下:
 
   location ~ /5b.txt {
          return 501
   }
 
   location ~* /5b.txt {
          return 504
   }  
 
測試結果為:
http://192.168.1.80/5b.txt ------------501
http://192.168.1.80/5B.txt ------------504
 
將順序反下,將~*放前面
   location ~* /5b.txt {
          return 501
   }
 
   location ~ /5b.txt {
          return 504
   }  
 
測試結果為:
http://192.168.1.80/5b.txt ------------501
http://192.168.1.80/5B.txt ------------501
 
結論: 去分和不區分大小寫的正則匹配優先順序相同,以先後順序來決定匹配哪一個.
 
2)再來比較=與~的優先順序
 
   location = /5b.txt {
          return 502
   }
 
   location ~ /5b.txt {
          return 504
   }  
 
測試結果為:
http://192.168.1.80/5b.txt -------------502
 
結論:=的優先順序比~高
 
3)再來比較下 ^~ 與 ~的優先順序
 
   location ~ /5b.txt {
          return 502
   }
 
   location ^~ /5b.txt {
          return 504
   }  
 
測試結果為:
http://192.168.1.80/5b.txt  --------------504
 
結論:^~的優先順序比~高
 
 
4)再測試 ^~ 與 = 的優先順序
 
   location ^~ /5b.txt {
          return 502
   }
 
   location = /5b.txt {
          return 504
   }  
 
測試結果:
http://192.168.1.80/5b.txt  --------------504
 
結論:=的優先順序比 ^~高
 
5)再來測試^~同級之間的優先順序
 
   location ^~ 5b.txt {
          return 502
   }
 
   location ^~ /3a/ {
          return 504
   }
 
 
測試結果:
http://192.168.1.80/5b.txt  --------------504
 
結論 :^~優先匹配的是從根開始的匹配
 
6)再來看空格(即什麼都不加)與~的優先順序比較
 
   location /5b.txt {
          return 502
   }
 
   location ~ /5b.txt {
          return 504
   }
 
測試結果為:
http://192.168.1.80/5b.txt  --------------504
 
結論:空格的優先順序比~低
 
優先順序排序為:
空格(即不加)
~與*~正則匹配的優先順序按先後次序來決定的
^~同級之間的匹配是按照根目錄順序來的
 
------------------------------------------------------------------------------------------
再來看看下面的例子:
 
1)精確匹配,即“=”
location = /images/test.png {
    echo 'config1';
}
 
location  /images/test.png {
    echo 'config2';
}
 
location \/images\/test\.png$ {
    echo 'config3';
}
 
如果此時請求 http://127.0.0.1/images/test.png 輸出的內容是config1, 毋容置疑,精確匹配優先順序最高!
 
2)精確匹配的特殊情況
location = / {
    index index.html;
}
 
location / {
    echo 'config2';
}
 
此時輸入http://127.0.0.1 輸出的內容是config2, 怎麼精確匹配的優先順序不靈了呢?
是這樣的,精確匹配還是起作用了,請求目錄(非具體檔案),nginx會將請求內部定向到index檔案,
既此時真正的請求是http://127.0.0.1/index.html, 這是config2則被命中!
所以精確匹配不要用來匹配/
 
3)字串搜尋與正則搜尋
location /images/test.png {
    echo 'config1';
}
 
location ^~ /images/ {
    echo 'config2';
}
 
location ~ \/images\/test\.png$ {
    echo 'config3';
}
 
location ~ \/images\/ {
    echo 'config4';
}
 
如果此時請求http://127.0.0.1/images/test.png 輸出的內容是config3,正則命中。
(雖然config1為最長匹配的字串,此時只做記錄,後面還要搜尋正則匹配,則config3正則匹配命中),
仔細觀察可以發現config4也被匹配成功了,但是正則的匹配順序是按照location的定義順序匹配的,所以config3命中.
 
4)字串匹配優先順序的提升( ^~ )
location /images/ {
    echo 'config1';
}
 
location ^~ /images/test.png {
    echo 'config2';
}
 
location ~ /images/test\.png$ {
    echo 'config3';
}
 
location ~ \/images\/ {
    echo 'config4';
}
 
如果此時請求 http://127.0.0.1/images/test.png 輸出的內容是config2, 首部匹配命中。
(因為字串匹配是優先搜尋的,此時發現config2 為最長的字串匹配且為^~匹配方式,所以停止搜尋正則,直接命中!)
 
所以這裡的 ^~ 符號比較特殊,就是為了提高字串匹配的優先順序,優先於正則匹配.
------------------------------------------------------------------
/ 通用匹配,任何請求都會匹配到。
多個location配置的情況下,需要遵循:
首先匹配=
其次匹配^~
再其次按照配置檔案的順序進行正則匹配、
最後是交給/進行通用匹配
注意:當有匹配成功時,立刻停止匹配,按照當前匹配規則處理請求

看看下面匹配規則:
#規則A
location = / {
}

#規則B
location = /login {
}

#規則C
location ^~ /static/ {
}

#規則D
location ~ \.(gif|jpg|png|js|css)$ {
}

#規則E
location ~* \.png$ {
}

#規則F
location !~ \.xhtml$ {
}

#規則G
location !~* \.xhtml$ {
}

#規則H
location / {
}

那麼產生的效果如下:
1)訪問根目錄/, 比如http://localhost/ 將匹配規則A
2)訪問 http://localhost/login 將匹配規則B,http://localhost/register 則匹配規則H
3)訪問 http://localhost/static/a.html 將匹配規則C
4)訪問 http://localhost/a.gif, http://localhost/b.jpg 將匹配規則D和規則E,但是規則D順序優先,規則E不起作用,而 http://localhost/static/c.png 則優先匹配到規則C
5)訪問 http://localhost/a.PNG 則匹配規則E,而不會匹配規則D,因為規則E不區分大小寫。
6)訪問 http://localhost/a.xhtml 不會匹配規則F和規則G,http://localhost/a.XHTML不會匹配規則G,因為不區分大小寫。規則F,規則G屬於排除法,符合匹配規則但是不會匹配到,所以想想看實際應用中哪裡會用到。
7)訪問 http://localhost/category/id/1111 則最終匹配到規則H,因為以上規則都不匹配,這個時候應該是nginx轉發請求給後端應用伺服器,比如FastCGI(php),tomcat(jsp),nginx作為方向代理伺服器存在。

注意:在實際使用中,至少清楚下面匹配規則
1)直接匹配網站根,通過域名訪問網站首頁比較頻繁,使用這個會加速處理,官網如是說。
2)這裡是直接轉發給後端應用伺服器了,也可以是一個靜態首頁

第一個必選規則:
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/
}

看看下面幾個設定

# 重寫跳轉
rewrite  "^/conference/([^/]+)$" /con_detail.php?con_title=$1 last;
rewrite  "^/conference/([^/]+)/$" /con_detail.php?con_title=$1 last;

#遮蔽爬蟲
if ($http_user_agent ~* "qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Feedfetcher-Google|Yahoo! Slurp|Yahoo! Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot|ia_archiver|Tomato Bot")
    {
    return 403;
    }

#favicon.ico不用打日誌
   location = /favicon.ico {
      log_not_found off;
      access_log off;
    }

#不允許訪問隱藏檔案
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

#訪問圖片,flash檔案等不用打日誌
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
    expires      7d; #檔案返回的過期時間是7天
    access_log off;
}

#訪問js和css檔案不用打日誌
location ~ .*\.(js|css)?$ {
    expires      1d; #檔案返回的過期時間是1天
    access_log off;
}


#設定php-cgi
location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    #攔截不存在的php頁面請求
    if (!-f $document_root$fastcgi_script_name) {
        return 404;
    }

}

相關文章