约定
本文以 Nginx 1.17.6 主线版为准。
引言
location 是 Nginx 配置中的重要一环,用来配置动静分离、反向代理等功能。
而 location 匹配规则,网上有太多错误的说法,今予以纠正并给出正确规则描述。
最常见的错误
最常见的错误之一,就是认为 ^~ 的优先级高于 ~,但实际上,我们编写如下配置:
1server { 2 listen 80; 3 4 location / { 5 return 600; 6 } 7 8 location /a { 9 return 601; 10 } 11 12 location ^~ /a/b { 13 return 602; 14 } 15 16 location /a/b/c { 17 return 603; 18 } 19 20 location ~ (star)$ { 21 return 604; 22 } 23}
并编写程序验证(或者找个 HTTP 客户端软件验证),
1import requests 2 3 4class App: 5 @staticmethod 6 def main(): 7 pre = 'http://192.168.1.162' 8 uris = ['/a/b/c/star', '/a/b/d/star'] 9 for uri in uris: 10 resp = requests.get(pre + uri) 11 code = resp.status_code 12 print('请求 {:s} 的结果的状态码为 {:d}'.format(uri, code)) 13 resp.close() 14 15 16if __name__ == '__main__': 17 App.main()
输出结果为:
1请求 /a/b/c/star 的结果的状态码为 604 2请求 /a/b/d/star 的结果的状态码为 602
由此可见,^~ 与 ~ 并非简单的优先级关系,它们之间真正的关系是:如果匹配到的前缀匹配项中最长的一个带有 ^~,则不再匹配正则(~)。
上面的例子中,/a/b/c/fuck 匹配到的前缀匹配项有 /a 和 /a/b 和 /a/b/c,而最长的 /a/b/c 不带 ^~,所以继续匹配正则,从而匹配到 ~ (fuck)$。
而 /a/b/d/fuck 匹配到的前缀匹配项有 /a 和 /a/b,最长的 /a/b 带 ^~,所以不再匹配正则。
匹配规则
给出几个定义:
1精确匹配项:带 = 修饰的 location 2前缀匹配项:不带符号修饰的 location 3正则匹配项:带 ~ 或 ~* 修饰的 location,它们唯一的区别是前者区分大小写,后者不区分
事实上,nginx 的 location 规则很难排出优先级,只能用文字描述:
只考虑了比较常用的 =、^~、~、~*,其他的读者自行查阅。
- 先匹配
=,匹配到则停止,否则继续。- 再匹配前缀匹配项,如果:
(a)有匹配项被匹配到,且最长的匹配项有 ^,则停止匹配;,则进行正则匹配;
(b)有匹配项被匹配到,且最长的匹配项没有 ^
(c)没有匹配项被匹配到,则进行正则匹配。- 然后进行正则匹配(不分长短,写在前面的优先),一旦匹配到则将其作为匹配结果(上一步的结果被替换)。
最后,如果始终没有匹配成功则返回 404。