Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >URL是如何关联Nginx location配置块的?

URL是如何关联Nginx location配置块的?

作者头像
陶辉
发布于 2023-10-18 03:19:08
发布于 2023-10-18 03:19:08
41300
代码可运行
举报
文章被收录于专栏:陶辉笔记陶辉笔记
运行总次数:0
代码可运行

上一篇文章介绍了HTTP请求匹配server{ }配置块的过程,接着请求会继续匹配location{ }配置块,并最终决定哪些指令及Nginx模块处理请求。本文将介绍location的匹配规则,以及rewrite指令与location匹配顺序的关系。

生产环境中的nginx.conf往往含有上百条location,这是因为Nginx常常身兼多职:充当提供静态资源CDN、作为负载均衡为分布式集群提供扩展性、作为API gateway提供接口服务等等。location一旦配置错误,Nginx上巨大的并发连接数会将错误放大上万倍,很容易导致严重的线上事故。

而location也很容易配置错误,它既支持前缀匹配,也支持正则表达式匹配,当二者同时出现时,为了获得更高的性能,Nginx设计了复杂的location匹配优先级。这是因为前缀匹配是对静态的location多叉树检索完成的,它的性能要比正则表达式高得多,唯有搞清楚具体的匹配流程,我们才能设计出匹配速度更快的location。

而且rewrite指令修改URL的功能也让location匹配变得更为复杂。特别是rewrite出现在server{ }和location{ } 里,会导致完全不同的结果。设计location时,我们还需要考虑到rewrite的效率,以及它是否会导致循环重定向。

这篇文章将从底层讲清楚URL匹配location { }配置块的流程,以及rewrite指令修改URL后,Nginx又是怎样重新匹配location的。

如何匹配前缀location?

location { }中定义了哪些Nginx模块会处理以及如何处理HTTP请求,因此,URL与location的匹配关系到功能的正确性,它是学好Nginx的必要条件。

location有两类匹配URL的方式,一类是前缀匹配,一类是正则表达式匹配。我们先来看前缀匹配。

URL通过/正斜杠符号分隔对象,因此URL从前至后具有天然的层级关系。比如,/wp-content/uploads/2019/07/test.jpg就具备以下意义:第1级wp-content说明它属于wordpress的内容,第2级uploads说明这是用户自行上传的文件,第3、4级2019/07描述了它的上传日期,第5级则是文件名称及格式。所以,从前至后进行前缀匹配最自然不过,像location /wp-content/uploads { } 就可以匹配wordpress中所有用户上传的文件。

当请求同时匹配上多个location时,Nginx会选择前缀最长的location { }处理请求。比如,location /wp-content/uploads { }和location /wp-content/uploads/2019 { }同时存在时,/wp-content/uploads/2019/07/test.jpg请求只会命中后者。最长前缀匹配,是location匹配的核心原则。

由于许多location处于包含关系,因此很容易出现重复匹配,那么,当数百个前缀location同时配置时,Nginx怎样基于最长前缀原则,最有效率的关联URL呢?事实上,Nginx会在启动过程中,将server{ }内的所有location基于前缀的包含关系,建立一颗多叉树。

比如,如下12个location将会构造出1颗4层的静态树,其中子树中的所有location,都是比父结节更长的前缀location;在同一层的结点中,它们互不相属,但却是基于字母表有序的(注意,同级location的排序与长度无关)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
location /test {root html;}
location /res {root html/res;}
location /res/img {root html/res/img;}
location /res/video {root html/res/video;}
location / {root html/res;}
location /resource/js {root html/res;}
location /resource/image {root html/res;}
location /his {root html/res;}
location /his/20 {root html/res;}
location /his/2020 {root html/res;}
location /his/20/02 {root html/res;}
location = /50x.html {}

举个例子,location = /50x.html和location /res都是/结点的子结点,因此它们处于树的第2层。且因为首字母5的ASCII码比r要小,因此50x.html是res的左兄弟结点。为了提高检索效率,Nginx会在构造树的过程中,取每一层兄弟结点中间的那一个,作为父结点的直接子结点。就像50x.html、his、res、test四个结点并存时,res将作为/的直接子结点,这能够减少检索的时间复杂度。

我们以一个具体的例子来看下location树的匹配流程。比如/his/2001/test.jpg请求到达时,它的匹配顺序如下图蓝色箭头所示:

事实上,/his/2001/test.jpg请求的匹配共包含6步:

  1. 请求首先命中/,暂时/将被设置为最长前缀,再进入子树看看有没有更长的前缀;
  2. 未匹配上直接子结点res,由于h在字母表的顺序小于r,因此到左兄弟结点his中继续匹配;
  3. 匹配上his后,此时/his被设置为最长前缀;
  4. 匹配上直接子树/his/20,将其设为最长前缀,仍然进入子树尝试更长的前缀匹配;
  5. 未匹配上直接子树20,由于1在字母表的顺序中小于2,因此到左兄弟结点中去看看;
  6. /20未匹配命中,且在字母表中/先于1,匹配到此结束。这时,最长匹配是/his/20,于是使用此location处理请求/his/2001/test.jpg。

这样我们搞清楚了最长前缀匹配的底层逻辑,接下来再来看正则表达式location的用法。

如何匹配正则表达式location?

当遇到前缀匹配无法覆盖的URL时,可以使用正则表达式匹配请求。当然,与上一篇介绍过的server_name类似,使用正则表达式的前提是将pcre开发库编译进Nginx。一次写对正则表达式很难,在Linux下我建议你用pcretest命令行工具提前测试正则表达式。关于正则表达式和pcretest工具的用法,你可以观看下我在极客时间上的视频课程《Nginx核心知识100讲》第46课《Nginx中的正则表达式》

在location中使用正则表达式,只需要在表达式前加入或者*符号,其中前者表示字母大小写敏感,而后者对大小写不敏感,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
location ~* *\.(gif|jpg|png|webp|)$

它可以匹配各类图片,且忽略文件格式后缀的大小写。

多个正则表达式location之间的匹配次序很简单,按照它们在server{ }块中出现的位置,依次匹配,直接使用最先命中的location即可。所以使用正则表达式要小心,当上方的正则表达式匹配范围过大时,下方的正则表达式location可能永远也无法命中

当正则表达式与前缀location同时出现时,事情就变得复杂起来。我们前面介绍过,前缀location构成的多叉树匹配效率很高,而正则表达式的匹配要慢得多。因此,Nginx会优先进行前缀location匹配,再进行正则表达式location的匹配,而且Nginx额外给前缀location提供了2个跳过正则表达式匹配的武器:=和^~。

在执行前缀匹配时,如果URL与location完全相等,那么Nginx不会再检索子树寻找更长的前缀匹配,但还会执行正则表达式匹配。如果你希望URL完全相等后,不必再匹配正则表达式location,那么可以在location前增加=号。比如,当location = / {}与location / {}同时出现时,前者是为了匹配访问首页的请求,而后者可以匹配任何请求,常用来兜底。因此,如果某些页面访问频率非常高,你应该用=号加快location的匹配速度

另外,^也可以跳过正则表达式匹配阶段,加快location的执行速度,而且它比=号的应用范围更广,^不需要URL完全相等,只需要匹配上前缀即可跳过后续的正则表达式。注意,只有最长匹配上携带^~符号,才能够跳过正则表达式。比如,你觉得/res/blog/js/1.js访问下面3个location时会获得什么响应?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
location ^~ /res/blog {return 200 'res blog';}
location /res/blog/js {return 200 'res blog js';}
location ~* .*\.js {return 200 'js';}

答案是’js’!虽然这个请求同时命中了3个location,但2个前缀location中,/res/blog虽然带有^~符号,可惜它却不是最长的前缀匹配;而/res/blog/js虽然是最长前缀,但又不能阻止正则表达式;*最终第3个location ~ .*.js匹配上了URL! **

简单的总结下location匹配规则(见下图):

  1. 先对前缀location执行最长前缀匹配
  2. 若最长前缀location前,携带有=或者^~,那么使用此location配置块处理请求;
  3. 按server{ }中正则表达式的出现顺序,依次匹配。成功后就选中此location;
  4. 若所有正则表达式皆未匹配上,则使用第1步中检索出的最长前缀location处理请求。

你可能会问,如果第1步中就没有找到能匹配上的前缀location,那该怎么办?很简单,Nginx会直接返回404。当然,为了避免这种情况发生,通常我们都会添加location / { }兜底,它可以匹配任意URL。

注意:location中的正则表达式,就像server_name中一样,可以用小括号()提取变量,供后续其他Nginx模块的指令使用。

配置location时,还有一个技巧需要你掌握:由于客户端的URL中可能含有重复的正斜杠/,因此Nginx会自动合并连续的重复正斜杠/。比如,//res/blog///a.js会被合并成/res/blog/a.js。如果你想关闭这一功能,可以添加下面这行配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
merge_slashes off;

由于location的匹配规则相当复杂,所以Nginx会在debug级别的日志中,打印出最终选中了哪个location。比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
test location: "/"
test location: "res"
test location: "/blog"
using configuration "/res/blog"

其中,using configuration指明了最终选择了哪个location。当然,要想开启debug日志,除了在nginx.conf里将error_log的日志级别设为debug外,还需要在configure时加入了—with-debug选项。

rewrite指令是如何工作的

虽然我们已经清楚了location的匹配规则,但是,匹配的URL未必是客户端的原始URL,因为rewrite指令可以修改URL!因此,我们还需要了解rewrite指令的用法,这样才能全面掌握location的匹配规则。

当系统升级、维护或者数据迁移时,往往需要重写URL后,再执行location匹配。rewrite指令就是用来重写URL的,它的用法非常简单,比如下面这行指令就可以将/reg1/a.js修改为/reg2/a.js:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
rewrite /reg1/(.*) /reg2/$1;

显然,rewrite可以反复地修改URL,并导致location被反复匹配命中。因此,为了防止不当的rewrite指令导致死循环,Nginx在代码层面将1个请求的rewrite次数限制为10次,超过后会直接返回500错误码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define NGX_HTTP_MAX_URI_CHANGES           10

rewrite指令既可以直接出现在server{ }块中,也可以出现在location { }块中,但它们的工作流程却完全不同!比如,你觉得下面的rewrite会导致请求/reg1/a.js无限循环吗?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
rewrite /reg1/(.*) /reg2/$1;
location /reg2 {rewrite /reg2/(.*) /reg1/$1;}

其实不会,因为server{ }中的rewrite指令只会执行1次。要说清楚rewrite、location的执行时机,我们得先清楚HTTP请求的11个执行阶段。

当Nginx接收完HTTP头部后,会让各Nginx模块基于Pipe And Filter模型依次处理请求。其中,为了让模块的处理次序更加可控,Nginx基于Web语义将其分为11个阶段,每个Nginx模块通常会选择1个阶段介入请求的处理流程。rewrite与location涉及到其中的4个阶段,下面看看它们究竟做了些什么:

我们依次分析这4个阶段:

  1. server{ }块中的rewrite指令,将在NGX_HTTP_SERVER_REWRITE_PHASE阶段执行。从图中可以看到,它只会执行1次;
  2. 前2节介绍的location匹配流程,就发生在NGX_HTTP_FIND_CONFIG_PHASE阶段;
  3. location{ }块中的rewrite指令,在NGX_HTTP_REWRITE_PHASE阶段执行;
  4. NGX_HTTP_POST_REWRITE_PHASE阶段中,判断location中的rewrite指令是否重写了URL,如果是,那么跳转到NGX_HTTP_FIND_CONFIG_PHASE阶段再做1次location匹配,否则继续向下,由其他Nginx模块处理请求。

因此,不同于server{ }块,location中的rewrite指令是可能反复执行多次的。

其实,rewrite指令还可以携带4种不同的flag参数,它还将影响if、set等其他脚本类指令的执行。本文聚焦于location的匹配,后续我在脚本指令的介绍文章中,还会讲到rewrite指令的其他用法。

小结

本文介绍了HTTP请求匹配location的流程。

location支持URL按最长前缀进行location匹配。Nginx启动时会将所有前缀location构造出一颗静态的多叉树,其中子树中的结点都是父结点的更长前缀,而兄弟结点间则按字母表排序。这样,前缀URL的匹配效率就很高。

相比起来,正则表达式则按照在nginx.conf中的出现顺序进行匹配,效率要低得多。当二者同时出现时,虽然正则表达式优先级更高,但=号和^~号可以让前缀location跳过正则表达式匹配,提升性能。然而这样让location匹配更容易出错,如果你在开发环境中,可以借助debug级别的error.log日志,通过using configuration确认Nginx究竟选择了什么location来处理请求。

rewrite指令可以反复修改URL,其中server{ }块中的rewrite指令只会执行1次,而location中的rewrite则可能最多执行10次,超出后Nginx会返回500错误码。只有理解了11个HTTP阶段的执行顺序,才能掌握rewrite与location的匹配关系。

你可能知道,location { }配置块内可以嵌套location { },虽然这不是一种推荐的配置方式,但它确实是被语法规则支持的。那么,在嵌套发生时,基于本文的理论,location是如何匹配的?rewrite指令又是怎样工作的?欢迎你在帖子下方留言,与我一起探讨更好的热部署实现方案。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-08-092,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
nginx location if 的匹配规则
~      #波浪线表示执行一个正则匹配,区分大小写 ~*    #表示执行一个正则匹配,不区分大小写 ^~    #^~表示普通字符匹配,不是正则匹配。如果该选项匹配,只匹配该选项,不匹配别的选项,一般用来匹配目录 =      #进行普通字符精确匹配 @     #"@" 定义一个命名的 location,使用在内部定向时,例如 error_page, try_files
拓荒者
2019/03/11
12.9K0
Nginx的location配置规则梳理
Nginx几乎是当下绝大多数公司在用的web应用服务,熟悉Nginx的配置,对于我们日常的运维工作是至关重要的,下面就Nginx的location配置进行梳理: 1)location匹配的是nginx
洗尽了浮华
2018/01/23
2.1K0
nginx的location、rewrite玩法详解
顺序 no优先级: (location =) > (location 完整路径) > (location ^~ 路径) > (location ~,~* 正则顺序) > (location 部分起始路径) > (/)
sunsky
2020/08/20
1.2K0
Nginx location匹配规则
从上面的语法出发,可以了解到 location 可以区分为三个部分,接下来一个一个的研究一下。
北国风光
2019/04/11
2.1K0
Nginx配置location总结及rewrite规则写法
location = / {     # 精确匹配 /,主机名后面不能带任何字符串     [ configuration A ]  } location / {     # 因为所有的地址都以/开头,所有这条规则将匹配到所有请求     # 但是正则和最长字符串会优先匹配     [ configuration B ] } location /documents/ {     # 匹配任何以/documents/开头的地址,匹配符合以后,还要继续往下搜索     # 只有后面的正则表达式没有匹配到时,这一条才会采用     [ configuration C ] } location ~ /documents/Abc {     # 匹配任何以 /documents/开头的地址,匹配符合以后,还要继续往下搜索     # 只有后面的正则表达式没有匹配到时,才会采用这一条      [ configuration CC ] } location ^~ /images/ {     # 匹配任何以/images/开头的地址,匹配符合以后,停止往下搜索正则,采用这一条     [ configuration D ] } location ~* \.(gif|jpg|jpeg)$ {     # 匹配所有以gif,jpg或jpeg结尾的请求     # 然而,苏朋友请求/images/下的图片会被config D处理,因为^~到达不了这一正则     [ configuration E ] } location /images/ {     # 字符匹配到 /images/,继续往下,会发现^~存在     [ configuration F ] } location /images/abc {     # 最长字符匹配到/images/abc,继续往下,会发现^~存在     # F与G的放置顺序是没有关系的     [ configuration G ] } location ~ /images/abc/ {     # 只有去掉config D才有效:先最长匹配config G开头的地址,继续往下搜索,匹配到这一正则,采用     [ configuration H ] } location ~* /js/.*/\.js
星哥玩云
2022/07/29
1.1K0
Nginx http相关常用配置总结
设置允许的客户端请求体大小最大值,请求头域Content-Length指明的值。如果请求体大小超过配置设置值,返回413错误给客户端。需要注意的是,浏览器不定义可以正确的展示该错误。设置client_max_body_size 为0,禁用请求体大小检查。
授客
2019/09/11
1.5K0
Nginx - location中的匹配规则和动态Proxy
https://nginx.org/en/docs/http/ngx_http_core_module.html#location
小小工匠
2024/05/26
1.4K0
Nginx - location中的匹配规则和动态Proxy
实用篇-无处不在的Location
location配置是nginx模块化配置中最出色的一个设计,几乎所有nginx的业务场景都要通过书写多个location配置来顺应业务需要。语法配置和执行规则都相对比较简单,完全可以掌握在脑海之中。
后端技术探索
2018/08/10
5230
Nginx中location、rewrite使用方法
矫正: location 的匹配顺序其实是“先匹配普通,再匹配正则”。我这么说,大家一定会反驳我,因为按“先匹配普通,再匹配正则”解释不了大家平时习惯的按“先匹配正则,再匹配普通”的实践经验。这里我只能暂时解释下,造成这种误解的原因是:正则匹配会覆盖普通匹配。
星哥玩云
2022/07/28
1.2K0
Nginx中location、rewrite使用方法
nginx中的location & root & alias & rewrite
注意: alias只能用于location中(使用alias,目录名后面一定要加“/”),而root可以用在http、server和location中。
阿dai学长
2019/04/03
2.8K0
Nginx Location
不知不觉 nginx主题的文章写了60+篇,有最早的也有最近的,有些是记录安装配置,有些是记录问题解决方法,内容质量有深也有浅参差不齐,随着技术迭代有些文章已经过时了(例如Docker时代)不再符合当前的技术需求,而有些文章虽然久远但是仍有有意义(例如Nginx HA),所以有了梳理这些文章的想法,目标有两个吧,一是回顾下过去的文章巩固下知识点,二是去其糟粕留下精华将有价值的文章搬迁(搬砖)的微信公众号。
用户1560186
2019/11/19
8240
Nginx正确配置Location
之前已经讲过Nginx的基本配置,本篇文章主要对Nginx中Location指令的作用进行介绍。本篇文章主要对Nginx的Location配置原则进行详细的讲述。Location是根据用户请求的URI来进行不同的定位,定位到不同的处理方式上,匹配成功即进行相关的操作。首先需要先介绍一下Nginx的echo模块,它可以配置的Location标签是否正确,是否达到配置的目的。
创译科技
2019/08/30
1.2K0
Nginx正确配置Location
Nginx Location和Rewrite深入剖析
Nginx由内核和模块组成,其中内核的设计非常微小和简洁,完成的工作也非常简单,仅仅通过查找配置文件将客户端的请求映射到一个location block,而location是Nginx配置中的一个指令,用于访问的URL匹配,而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作。
星哥玩云
2022/07/26
7450
【Nginx06】Nginx学习:HTTP核心模块(三)Location
Location 是整个 HTTP 模块中非常重要的一个子模块,它是为某个请求URI(路径)建立配置。这个模块又是属于 Server 模块的子模块,同时它还可以嵌套在另一个 Location 模块下面,因此,它的作用范围是 server 和 location 。其实,说白了,也就是我们可以为指定的一些路径去做一些额外的配置。
硬核项目经理
2023/08/09
9780
【Nginx06】Nginx学习:HTTP核心模块(三)Location
Nginx之Location
URL地址匹配是Nginx配置中最灵活的部分 Location 支持正则表达式匹配,也支持条件匹配,用户可以通过location指令实现Nginx对动丶静态网页的过滤处理。
随心助手
2019/10/15
6210
nginx之location指令
localtion可以由前缀字符串或正则表达式定义。正则表达式使用前面的“〜*”修饰符(不区分大小写匹配)或“〜”修饰符(用于区分大小写匹配)指定。要找到匹配给定请求的位置,nginx首先检查使用前缀字符串(前缀位置)定义的位置。默认情况, nginx先检查前缀字符串,然后检查正则表达式,如果前缀字符串匹配到了,并且前缀字符串有这个“^~” 要求,就不配正则了;如果没有这个“^~” ,即使前缀匹配到了,也要去匹配正则表则,如果正则表达式匹配到了,就是用正则表达式的,没有就是用前缀字符串匹配到的路径
山行AI
2019/08/05
1.5K0
Nginx Location 匹配规则
Nginx 的 location 用于匹配 URI 不同路径的请求,实现对请求的细分处理。例如当客户端请求 https://www.nginx-test.com/index.html 时,Nginx 使用本地的静态文件响应,而当客户端请求相同地址的 https://www.nginx-test.com/api 时,Nginx 将请求转发到后端服务器。
Se7en258
2021/07/01
1.5K0
Nginx葵花宝典—草根站长配置Nginx运维百科全书
前段时间把网站迁移到腾讯云,之前是lamp,现在改为lnmp,自以为nginx功底还可以,开发这么多年,平常环境都有配置。但是,但是,最近读站点做SEO优化,发现nginx很多地方不会配。比如:
周陆军博客
2023/06/06
8790
nginx配置 location及rewrite规则详解
1. location正则写法 语法规则: location [=|~|~*|^~] /uri/ { … } =    开头表示精确匹配 ^~  开头表示uri以某个常规字符串开头,理解为匹配 url路径即可。nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格)。 ~   开头表示区分大小写的正则匹配 ~*  开头表示不区分大小写的正则匹配 !~和!~*分别为区分大小写不匹配及不区分大小写不匹配 的正则 / 通用匹配,任何请求都会匹
用户1214487
2018/05/28
2.9K0
Nginx系列教程(6)Nginx location 匹配规则详细解说
Nginx 的 location 实现了对请求的细分处理,有些 URI 返回静态内容,有些分发到后端服务器等,今天来彻底弄懂它的匹配规则
haikangweishi
2020/04/04
1.7K0
相关推荐
nginx location if 的匹配规则
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验