nginx location匹配规则探究

简介

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

语法

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

匹配规则分类

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

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

  1. 前缀字符串

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

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

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

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

匹配流程

  • 流程图
  • 讲解
  1. 首先判断是否有=号精准匹配满足uri的需求,如果有的话,直接跳出匹配

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

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

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

  5. 如果正则匹配没有匹配到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中,百思不得其解

  • 实现方案
  1. 第一种 正则匹配 /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
  1. 第二种 都用正则匹配
## 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
  1. 第三种 更换正则匹配的顺序
## 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
        
  1. 第四种 用 ^~ 来匹配
## 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
  • 总结

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

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

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

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

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

  4. 方案第四种
    由于 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值