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