nginx location匹配规则探究

    技术2022-07-11  85

    文章目录

    简介语法匹配规则分类匹配流程匹配优先级官网示例 经典案例案例一案例二 参考链接

    简介

    location 是核心模块 ngx_http_core_module 提供的功能,为了将他的用法搞清楚,我们把官网的例子拿来试验

    语法

    语法: location [ = | ~ | ~* | ^~ | ] uri {...} location @name {...} 可以用在 server,location 中

    匹配规则分类

    location对格式化后的uri 根据给很粗的匹配规则进行匹配 请求的uri中可能存在的多个斜杠,格式化过程中,会被压缩成一个斜杠,比如//test//a///b 会压缩成/test/a/b

    location可以设置两种匹配规则 前缀字符串和正则表达式

    前缀字符串

    即直接把location /后面的字符串拿来匹配uri中的信息,遵循最长匹配规范,啥叫最长匹配规范,就是所有的前缀字符串都匹配一遍,能匹配上最长的字符串,作为候选

    = 精确匹配前缀字符串,匹配到后不再进行任何匹配操作^~ 遵循最长匹配规则,匹配前缀字符串,匹配到后,不再进行正则匹配 正则表达式

    是一组符合正则语法规范的字符串,用来匹配指定的uri包括

    ~ 大小写敏感匹配~* 大小写不敏感匹配类似于grep -i

    匹配流程

    流程图 讲解

    首先判断是否有=号精准匹配满足uri的需求,如果有的话,直接跳出匹配

    全部匹配一遍用前缀字符串设置的location规则,类似 location /test location = /test location ^~ /test这样色儿的,将他们全部匹配一遍后,其中可以匹配uri,且拥有最长字符串的location规则将被选中,被记录下来

    查看最长匹配的location 的修饰符是否是 ^~ 如果是的话,匹配成功,不再进行后续的正则匹配

    从上到下,按照顺序进行正则匹配,匹配成功一个,即跳出匹配

    如果正则匹配没有匹配到location 则走第2步中记录的最长匹配记录的location作为成功匹配的路线

    匹配优先级

    匹配的顺序是先进行普通匹配,然后再进行正则匹配

    匹配的优先级是

    "=" > "^~" > "~" > "~*" > /

    官网示例

    location = / { [ configuration A ] } location / { [ configuration B ] } location /documents/ { [ configuration C ] } location ^~ /images/ { [ configuration D ] } location ~* \.(gif|jpg|jpeg)$ { [ configuration E ] }

    说明

    The “/” request will match configuration A, the “/index.html” request will match configuration B the “/documents/document.html” request will match configuration C the “/images/1.gif” request will match configuration D the “/documents/1.jpg” request will match configuration E.

    经典案例

    案例一

    背景

    业务nginx中已经存在了一个~ /test的正则location配置,而现在又有了新需求,需要配置一个/test-be的location,我随便按照之前 /test 的配方配了一个,结果踩到了坑里面,无论怎么访问,请求都到不了服务!经过排查,发现所有请求都进了/test的lacation中,百思不得其解

    实现方案 第一种 正则匹配 /test 普通匹配 /test-be ## nginx 配置 location ~ /test { return 200 "/test"; } location /test-be { return 200 "/test-be"; } ##测试结果 [root@base-images-factory conf]# curl 127.0.0.1/test;echo /test [root@base-images-factory conf]# curl 127.0.0.1/test-be;echo /test 第二种 都用正则匹配 ## nginx 配置 location ~ /test { return 200 "/test"; } location ~ /test-be { return 200 "/test-be"; } ##测试结果 [root@base-images-factory conf]# curl 127.0.0.1/test;echo /test [root@base-images-factory conf]# curl 127.0.0.1/test-be;echo /test 第三种 更换正则匹配的顺序 ## nginx 配置 location ~ /test-be { return 200 "/test-be"; } location ~ /test { return 200 "/test"; } ## 测试结果 [root@base-images-factory conf]# curl 127.0.0.1/test;echo /test [root@base-images-factory conf]# curl 127.0.0.1/test-be;echo /test-be 第四种 用 ^~ 来匹配 ## nginx 配置 location ~ /test { return 200 "/test"; } location ^~ /test-be { return 200 "/test-be"; } #测试结果 [root@base-images-factory conf]# curl 127.0.0.1/test;echo /test [root@base-images-factory conf]# curl 127.0.0.1/test-be;echo /test-be 总结

    可以看到,只有第三种和第四种 方案实现了我们的目的,那么原因呢?

    其实看过上面的匹配流程,在看着案例就很简单了

    方案 第一种 uri=/test-be 进行全局前缀字符串匹配 显然 location /test-be在第一轮中,已经被匹配到,作为候选location已经被选中,不幸的是在接下来的正则匹配环节 ~ /test也命中了,直接胜出,导致/test-be的请求,转发到了/test的location下

    方案第二种 显然在第一轮前缀匹配的过程中,是没有命中的,然后进入第二轮正则匹配,由于是按照顺序匹配首先会碰见/test 结果直接匹配成功,直接调出匹配,使得/test-be的请求再次落入了/test location的陷阱

    方案第三种 结合方案第二种的分析,很容易能够得出为什么方案三是可以正确匹配到/test-be的

    方案第四种 由于 location 定义 /test-be 用了 ^~修饰符,那么在第一轮前缀字符串匹配到后,将不再继续进行正则匹配,所以/test-be请求转发到 /test-be的location中

    案例二

    背景 nginx请求中,所有以.gif .gpg .jpeg结尾的文件都进行某段逻辑处理 但是 /images/xx.gif 这个请求却无法匹配实现方案 location ~ /images { return 200 "/images"; } location ~* \.(gif|jpg|jpeg)$ { return 200 "$request_uri"; } [root@base-images-factory conf]# curl 127.0.0.1/images/123;echo /images [root@base-images-factory conf]# curl 127.0.0.1/a/1.jpg;echo /a/1.jpg [root@base-images-factory conf]# curl 127.0.0.1/images/123.jpg;echo /images 结论

    很显然, 配置文件中都是正则匹配, 匹配规则是顺序匹配,那么/images/xx.jpg这样的请求在碰到 ~/images的时候就会被拦下来,显然到不了 ~* \.(gif|jpg|jpeg)$ 这一层

    参考链接

    http://nginx.org/en/docs/http/ngx_http_core_module.html#location

    https://www.jianshu.com/p/38810b49bc29

    Processed: 0.019, SQL: 9