本文非完全原创, 更多的是将相关资料进行整理
Last-Modified: 2019 年 5 月 10 日 15:28:29
- linux Nginx 配置篇:location 的匹配规则(附测试验证过程)
语法 |
匹配规则 |
空 |
普通匹配(遵循最大前缀匹配规则, 优先度比正则低) |
= |
精确 (严格) 匹配, 优先度最高 后续不再匹配正则
|
^~ |
非正则匹配 ( 依然遵循 最大前缀匹配规则) 后续不再匹配正则
|
~ |
表示区分大小写的正则匹配 |
~* |
表示不区分大小写的正则匹配 |
/ |
通用匹配,任何请求都会匹配到 (本质上等同于语法 空 ) |
!~
和 !~*
分别为区分大小写不匹配及不区分大小写不匹配 的正则, 但是是用于条件判断的时候(即 if
语句)
if ($host !~* "^www\.") {# ...}
匹配简单来说:
-
优先 匹配
=
精确匹配, 若未匹配到则转下一步骤
- 依照 最大前缀匹配 规则, 先匹配 普通规则 (
空
, ^~
)
若最终匹配到 ^~
, 则使用
若匹配到 空
或 未匹配到, 则转下一步骤(当前匹配结果暂时保存)
- 按照物理存储顺序, 若匹配到 任意一条正则, 马上使用(无视后面正则)
若未匹配到任意正则, 则使用步骤 2 中匹配到 普通正则
只有两类:正则 location 和普通 location
~
和 ~*
为正则 location
=
、^~
、@
和无任何前缀的都属于普通 location,另外,@
是用作服务端内部的一种转发行为,很少用,在此不做讨论。
- 先普通,再正则
- 普通 location 之间的匹配顺序:按最大前缀匹配
如location /a/{}
,location /a/b/ {}
, 请求 http://a/b/c.html 匹配的是 location /a/b/ {}
- 正则 location 之间的匹配顺序:按配置文件中的物理顺序匹配,只要匹配到一条正则,就不再考虑后面的
- 若普通 location 匹配到 精确匹配
=
或 非正则匹配 ^~
, 则不再进行后续的正则匹配
-
普通 location 与正则 location 之间的匹配结果选择
- 普通 location 先匹配,匹配到了结果,只是一个临时结果;
-
会继续正则 location 的匹配,
- 如果匹配到正则,则用匹配到的正则结果;
- 如果没有匹配到正则,则继续用普通匹配的那个结果
综上,常规的顺序是匹配完普通 location,还要继续匹配正则 location,但是,也可以告诉 nginx,匹配到了普通 location,就不要再搜索匹配正则 location 了,通过在普通 location 前面加上 ^~
符号,^
表示非,~
表示正则,^~
就是表示不要继续匹配正则。
除了 ^~
,=
也可阻止 nginx 继续匹配正则,区别在于 ^~
依然遵循最大前缀匹配规则,而 = 是严格匹配
location / {}
和 location =/ {}
的区别
/ {}
作为普通匹配,是遵循最大前缀匹配原则的,所以,对于一个 url,如果有更特殊合适的匹配,就选特殊合适的,如果没有更特殊合适的匹配,也有 / {}
兜着,就像是默认配置一样
=/ {}
遵循的是严格匹配规则,只能匹配到 http://ip:port/,同时会停止搜索正则匹配。
接下来测试验证。
1. 先验证第二条:普通 location 之间的匹配顺序:按最大前缀匹配
nginx.conf 配置:
# 普通 location
location /a/b {return 666;}
#普通 location
location /a/b/c {return 777;}
测试链接:http://192.168.88.38/a/b,http 状态码为 666,符合预期。如图(后面的测试可自行 F12 打开浏览器控制台查看 http 状态码,不再截图):
测试链接:http://192.168.88.38/a/b/c,http 状态码为 777,匹配的是 location /a/b/c {return 777;},符合预期。
2. 验证第三条:正则 location 之间的匹配顺序:按配置文件中的物理顺序匹配,只要匹配到一条正则,就不再考虑后面的
nginx.conf 配置:
location ~* /a {return 999;}
#匹配 a - z 的任意一个字母
location ~* ^/[a-z]$ {return 666;}
测试链接:http://192.168.88.38/a,http 状态码 999,匹配的是 location ~* /a {renturn 999;},符合预期。
将 nginx.conf 中的两个正则匹配顺序调换下:
location ~* ^/[a-z]$ {return 666;}
location ~* /a {return 999;}
测试链接:http://192.168.88.38/a,http 状态码 666,匹配的是 location ~* ^/[a-z]$,符合预期。
3. 验证第 4 条,其实第 4 条就相当于是总结性的匹配顺序了:
nginx.conf 配置:
# 普通 location
location /a {return 666;}
#普通 location
location /a/b {return 777;}
#正则 location
location ~* /a/b {return 888;}
测试链接:http://192.168.88.38/a,http 状态码 666,匹配到普通 location,location /a {return 666;}, 符合预期。
测试链接:http://192.168.88.38/a/b,http 状态码 777,先进行普通 location 匹配,遵循最大前缀原则,匹配到 location /a/b {return 777;},但是,这只是一个临时结果,因为接下来还要继续往下进行正则 location 匹配,匹配到 location ~* /a/b {return 888;},最终返回结果为 888。符合预期。
将正则 location 的规则改下:
# 普通 location
location /a {return 666;}
#普通 location
location /a/b {return 777;}
#正则 location
location ~* /a/c {return 888;}
测试链接:http://192.168.88.38/a/b,http 状态码 777,匹配到 location /a/b {return 777;},并且因为接下来没有符合的正则 location,所以最终返回为 777,符合预期。
综上,location 的匹配顺序及结果取值都符合 2,3,4 点结论。
接下来再测试验证普通 location 中的 ^~ 及 = 符号对于匹配搜索过程的阻断效果,当然,别忘了这俩符号的真实作用。^~ 为普通字符匹配,= 为精确匹配。
^~ 测试验证 nginx.conf 配置:
location /a {return 666;}
#普通匹配
location ^~ /a/b {return 777;}
#正则 location
location ~* /a/b {return 888;}
测试链接:http://192.168.88.38/a/b,匹配到 location ^~ /a/b {return 777;}后,因为使用了 ^~ 符号,不再继续搜索正则 location 匹配,所以,虽然下面有符合条件的正则 location,但是最终还是返回了 777,符合预期。
= 测试验证 nginx.conf 配置:
location /a {return 666;}
#普通匹配
location = /a/b {return 777;}
#正则 location
location ~* /a/b {return 888;}
测试链接:http://192.168.88.38/a/b,匹配到 location = /a/b {return 777;}后,因为使用了 = 符号,不再继续搜索正则 location 匹配,最终返回 777,符合预期。
另附上常用正则表达式:
- .:匹配除换行符外的任意字符
- ?:重复 0 次或 1 次
- :重复 1 次或更多次
- :重复 0 次或更多次
- d:匹配数字
- ^:匹配字符串的开始
- $:匹配字符串的结束
- {n}:重复 n 次
- {n,}:重复 n 次或更多次
- :匹配单个字符,如此处的字符 c
- [a-z]:匹配 a - z 小写字母的任意一个
- (a|b|c):匹配 a 或 b 或 c </span>