`
deyimsf
  • 浏览: 66638 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

深入理解location匹配规则

 
阅读更多

在前面几篇文章中,为了表述某个问题,我们都会举例说明,其中用的最多的都是以locaiton开头的配置例子,形式基本如下:

 

location / {

    // 一些指令

}

 

特别是在ngx中的变量中出现的最多,它就是我们这篇文章要介绍的核心内容。

 


1
什么是location

       

它是nginx基于http协议跟外界沟通的桥梁,用来匹配并执行一个规范化的(normalized)URI,任何http请求都需要经过它的“同意”才能通过nginx的大门。那究竟什么样的请求可以进入这个大门呢?这取决于location 后面配置的是什么样的规则,比如我们常见的一种配置:

 

location /a {

   return 200 “hi $uri”;

}

 

表示以“/a”开头的请求都可以进入,当你通过curl以“/a”进行访问的时候,你会明确得出一个结果:

 

curl http://127.0.0.1/a

hi /a

 

那如果请求是“/a/b”和“/b/a”呢?很显然,第一个可以明确输出

 

hi /a/b

 

因为它符合以“/a”开头的规则。第二个因为不符合规则所以就直接返回404了(但如果b 是nginx可感知的文件路径,并且该路径下有一个a文件,那么请求“/b/a”会返回这个文件,这部分内容会在后续文章中提到,目前先按404对待)。

 

到这里我们会看到,这个location有点像战争题材电视剧中的哨兵,而这个规则就是口令,为了防止敌人潜入部队,巡逻的士兵或站岗的哨兵会和来访人员进行口令对比,如果来访人员答不上来,那麻烦可就大了,具体是哪种麻烦这取决于部队。

 

回到nginx,它的实际匹配方式并没有像我们之前介绍的那样单一,它可以有多种匹配模式,它们之间并没有优略之分,你可以根据实际场景来选择任何你认为合适的模式对请求进行匹配。

 


2
location的六种匹配模式

 

在nginx中,每个location块在匹配uri时都可以指定一个修饰符,不同的修饰符对请求的匹配范围是不一样的。 除了上面简单介绍的无修饰符location外,nginx中还有多种location修饰符,总结起来的话可以分为六种,分别是【无】【=】【^~】【~*】【~】【@】,而且每种修饰符都有它特定的匹配方式和特性,具体有哪些不同,我们会在接下来的内容中对其一一解释。

 

2.1精确匹配

在nginx中有一种匹配模式叫精确匹配(或严格匹配),它只需要在location后面加上 “=”这样的修饰符,像这样:

 

location = /a {

    return 200 “hi $uri”;

}

 

这种其实更像我们理解中的部队中口令,需要严格匹配。比如部队规定今天的口令是“瞌睡虫”,那么当哨兵喊口令的时候,对方必须说“瞌睡虫”,多一个字不行,少一个字也不行。所以,回到我们这个例子,只有请求是“/a”的时候才会输出唯一正确的值:

 

curl http://127.0.0.1/a

hi /a

 

当你多一个字符(如“/a/”),或少一个字符(如“/”)都不会给出正确的输出,这既是精确匹配。

 

2.2普通匹配

另外一种是我在工作中经常用的无修饰符的location和很少使用的“^~”修饰符的location,我们管它叫“普通匹配”。

 

关于【无】修饰符的locaiton我们在最开始已经提到过了,现在我们用带“^~”修饰的location来举个例子:

 

location ^~ /a {

    return 200 “hi $uri”;

}

 

然后用请求“/a”来访问一下:

 

curl http://127.0.0.1/a

hi /a

 

看起来和【=】、【无】这两种模式匹配无差啊,再换个请求方式,用“/ab”试试:

 

curl http://127.0.0.1/ab

hi /ab

 

到这里可以肯定它跟【=】匹配是不一样的,如果是精确匹配的话会直接返回404的。

 

我们知道,无修饰符的location是以其后设置的uri作为前缀来匹配请求的,而就目前来看【^~】这种匹配模式也是可以完成【无】模式工作的,所以感觉这种修饰符应该还会另有他用,会不会是后缀匹配呢?用“/b/a”和“/ca”试试看:

 

curl http://127.0.0.1/b/a

curl http://127.0.0.1/ca

 

结果这俩货不约而同的都打出了404,显然我们的推测是错误的。

 

实际上【无】和【^~】修饰符单在匹配uri规则上是完全一样的,都是以其后紧邻的uri作为前缀来匹配请求的。要说清楚这个问题需要把下一小节讲的内容“正则匹配”提前拿过来,因为它们的区别就在对正则匹配的处理上,来看下面这个例子(这两个location在同一个nginx配置中):

 

location /a {

return 200 “hi $uri”;

}

 

location ~ /a {

return 200 “Hi I am regex”;

}

 

在上面的例子中,带“~”修饰符的location是可以正则匹配请求的,我们用请求“/a”来访问一下这个例子看会有什么效果:

 

curl http://127.0.0.1/a

Hi I am regex

 

通过结果可以看到,当前请求匹配的是支持正则匹配的那个locaiton,而且,似乎正则模式的匹配比无修饰符模式的匹配优先级要高。

 

那如果在无修饰符的location加上“^~”会是什么情况呢?来看实际例子(这两个location在同一个nginx配置中):

location ^~ /a {

return 200 “hi $uri”;

}

 

location ~ /a {

return 200 “Hi I am regex”;

}

 

此时我们用同样的请求“/a”来发起请求:

 

curl http://127.0.0.1/a

hi /a

 

怎么样?是不是有点“拨乱反正”的感觉,当你加上“^~”修饰符后你的location一下子就提高了匹配优先级。

 

此时有些读者可能会想,是不是因为带“^~”修饰符的location放在了带“~”修饰符location前面的原因?真的是这样吗?那我们把它反过来再看看:

 

location ~ /a {

return 200 “Hi I am regex”;

}

 

location ^~ /a {

return 200 “hi $uri”;

}

 

用同样的uri发起请求:

 

curl http://127.0.0.1/a

hi /a

 

可以看到,跟location配置未颠倒之前的打印结果是一样的。

 

至此我们可以得出一个结论,【无】、【^~】这两种匹配模式在uri的匹配上是完全一致的,都是以其后配置的uri作为前缀来匹配请求的。不同的是当nginx配置文件中同时存在正则匹配时,【无】的匹配优先级要低于正则匹配,而【^~】的匹配优先级要高于正则匹配。

 

2.3正则匹配

正则匹配,顾名思义,就是支持正则的location,在nginx中可以使用【~】或【~*】来标识,一旦打上这个标识,那么在其后的uri就变成了正则表达式。其中,【~】表示匹配的过程要区分大小写,而【~*】则表示不区分大小写,其它方面两者没有任何区别,所以为了节省篇幅,本节仅用【~】来举例说明。

 

关于正则匹配的具体使用规则,我们还是老规矩,先看例子:

 

location ~ /a {

      return 200 “hello”;

}

 

然后直接用“/a”发起一个请求

 

curl http://127.0.0.1/a

hello

 

?,跟之前介绍的匹配没区别?别急,再用“/b/a”试试:

 

curl http://127.0.0.1/b/a

hello

 

这样看是不是就有区别了?像“/b/a”这样的请求,之所以可以匹配上“/a”,是因为加上了“~”符号的“/a”变成了一个正则表达式,此时任何带“/a”的字符串都可以被这个表达式匹配。

 

上面这个例子虽然用到了正则匹配,但是并没有用到正则的一些专有符号,所以总是感觉少点什么,现在我们再来举几个例子来把这一点给补上,先看一个简单的,用正则匹配模拟普通匹配

 

location ~  ^/a {

    return 200  “hello”;

}

 

当前配置中的“^”符号表示匹配字符串的“行首”,也可以简单理解成从行首开始匹配,所以“^/a”的意思就是,匹配以/a开头的字符串。这种规则是不是跟普通匹配模式很像?来,curl验证一下:

 

curl http://127.0.0.1/b/a  -v

< HTTP/1.1 404 Not Found

 

curl http://127.0.0.1/a/b  

hello

  

可以看到,之前可以正常返回结果的请求“/b/a”此时却返回了404,而请求“/a/b”则因为是以“/a”开头,所以返回了正确的结果。

 

简单的例子说完了,再来看一个稍微复杂一点的,假设现在有这样一个需求:我们需要某个location只匹配以“数字”开头,结尾是“.html”,且数字的个数最多不能多于5个,最少不能少于1个。对于这样的一个需求,可以用下面的正则匹配来完成:

 

location ~ “^/\d{1,5}\.html$” {

      return 200 “hello”;

}

 

其中“^”符号在前面已经说过了,用来表示行首。而“\d”符号在正则表达式中表示0到9的任意一个数字,紧接着用“{1,5}”来表示这个数字可以出现1到5次。随后是我们约定的以“.html”结尾,所以需要在最后加一个“行尾”符“$”。最后我们看到在“.html”前还有一个“\”符号,这其实是一个转义符,因为“.”在正则表达式中表示一个任意字符,前面加上转义符“\”后,“.”就只是一个普通的符号了。解释了那么多,用几个请求验证一下:

 

curl http://127.0.0.1/1234.html

hello

 

curl http://127.0.0.1/12t34.html

< HTTP/1.1 404 Not Found

 

curl http://127.0.0.1/.html

< HTTP/1.1 404 Not Found

 

从输出可以看到,除了第一个返回了正确的结果,其它的请求都因为匹配失败返回404。

 

在正则中,除了上面提到的“常规”匹配,还有一种叫子匹配(子模式、group等),nginx中对这种子匹配也是支持的,我们来看一个在nginx中使用子匹配的例子:

 

location ~  ^/(.)(.)(.) {

       return 200 “hello -> [$1]-[$2]-[$3]”;

}

 

其中“.”表示任意单个字符;“()”表示一个子匹配,而“$1”、“$2”、“$3”则表示对应的子匹配中的内容;在这个例子里有三个“()”符号,表示一共有三个子匹配,“$”符号后加数字可以表示对应的子匹配内容。用几个请求来看一下效果:

 

curl http://127.0.0.1/1234

hello -> [1]-[2]-[3]

 

curl http://127.0.0.1/abcd

hello -> [a]-[b]-[c]

  

通过输出结果并对照上面的解释,应该会很容易的明白子匹配在nginx中的使用,另外一点需要注意的是,在nginx中,最多支持9个子匹配,且是前9个,后续的会被忽略。

 

最后,本节主要讲述了正则表达式在nginx中的使用,关于正则表达式本身的更详细的规则,读者只能自己google了。

 

2.4内部匹配

除了上面提到的几种匹配模式,nginx中还有一种特殊的匹配,我们可以叫他“内部匹配”,在配置文件中使用【@】表示。这里“内部”的意思是指外部用户看不到的location,目前nginx中只有几个指令能看到这种匹配(比如error_page、try_files等)。

 

好,我们针对上面这句开场白来做几个例子,看看它究竟是如何玩的,首先看一个正常的配置:

 

location /a {

      return 200 “hello”;

}

   

这个例子已经看到无数次了,对于“/a”这样的请求一定会打印出“hello”。现在我们把这个例子改成内部匹配模式,像这样:

 

location @/a {

   return 200 “hello”;

}

  

注意这个“@”符号和后面的“/a”中间没有空格,否则的话nginx无法启动成功,不要问为什么,这是“龟腚”。好,现在再用“/a”请求一下试试:

 

curl http://127.0.0.1/a  -v

 

< HTTP/1.1 404 Not Found

< Content-Type: text/html

< Content-Length: 175

 

意料之中,直接404了。有的同学可能会说,是不是你的请求不对,应该用“@/a”请求才对,但是仔细一想,这种请求显然是不成立的,因为它的完整url会是这样:

 

http://127.0.0.1@/a 

   

是不是很奇怪,这根本就不是一个合法的请求。

 

外部正常请求的方式失败了,那所谓的内部匹配方式又是怎么玩的呢?本节开始的时候我们说过,目前nginx中只有几种指令可以使用这种匹配,这其中就包括我们常用的“error_page”指令。该指令在nginx的作用是为某些错误响应码指定一个uri,意思是当某个请求的响应发生错误时,它的响应码正好被“error_page”指定,那么它的响应内容将是该指令后对应的uri展示的内容。还是有点绕,直接看例子把:

 

error_page  404  /a;

location /a {

   return 200 “This is a error page”;

}

 

当前配置中只有一个“/a”location规则,为了演示这个例子,我们需要用这种location匹配不到的请求来访问,比如“/b”,根据以往的经验,如果没有“error_page”指令,这种请求应该会返回404,而加上这个指令后效果是这样的:

 

curl http://127.0.0.1/b -v

 

< HTTP/1.1 404 Not Found

< Content-Length: 9

 

This is a error page

 

可以看到,响应码还是404,但是响应内容已经变成“/a”location所输出的内容了。

 

但是现在有一个问题,对于“/a”这样一个配置,只有在其他请求出错的时候才会用到,所以我们显然是不想让用户直接访问的,这个时候【@】这种方式就排上用场了,这个配置可以改成这样:

 

error_page  404  @/a;

location @/a {

    return 200 “This is a error page”;

}

 

此时再用“/b”试一下

 

curl http://127.0.0.1/b -v

 

< HTTP/1.1 404 Not Found

< Content-Length: 9

 

This is a error page

 

看结果显然是符合预期的,而原来的“/a”在配置中已经不存在。

 

实际上对于【@】这种匹配方式,在nginx中是完全独立存在的,其它模式都是从NGX_HTTP_FIND_CONFIG_PHASE阶段(后续有文章专门介绍,这里知道有这么一个东西就行,不必深究)开始匹配location的,而【@】则超三界之外,不在五行之中,直接走自己独立的匹配道路。它的内部实现逻辑大致是这样:当nginx的所有location被加载完毕后,所有带“@”的location会被单独放到一个容器中,任何支持这种location的指令,在使用的时候都是去这个容器中做完全匹配的。而且这种匹配并没有uri的概念,也就是说,我们在使用的时候并不需要把它伪装成uri,像这样就可以:

 

error_page  404  @a;

location @a {

    return 200 “This is a error page”;

}

 

同时它也没有什么特殊的匹配规则(比如以某个字符串前缀来匹配),从字符匹配上来说,跟精确匹配很像,多一个字符不行,少一个字符也不行,必须严格匹配。

 

另外一种跟【@】这种内部匹配规则很像的是带有internal指令的location,不同的是【@】只能被极少数指令使用,而带internal指令的location则适用与所有的内部请求,它不会更改原location的匹配流程(比如【@】方式会绕过NGX_HTTP_FIND_CONFIG_PHASE阶段,直接从单独容器中匹配),只会更改当前location的访问权限。

 

那什么是内部请求?大部分内部请求都符合这么一个特点:外部请求进入nginx后,都或多或少被nginx“倒过手”。比如rewrite指令,对于一个有着internal的location,外部请求是不能直接访问的,但是被rewrite重写uri后就可以,比如下面这个例子:

 

location /a {

    internal;

    return 200 “hello”;

}

 

location /b {

     rewrite /   /a;

}

 

当我们用curl访问“/a”请求时会直接返回404,而用“/b”请求则可以打印出正确的结果“hello”,原因是因为rewrite指令更改了当前请求的内外性质,把它变成了一个内部请求。

 

内部请求的实现原理大致是这样:nginx在配置阶段会把包含internal指令的location都打上一个内部标识(比如internal=1),然后,当某个请求成功匹配到该location后,nginx会先检查当前请求是否也被打上了内部标识(比如r->internal=1),如果没有,则拒绝请求,反之则通过。而上面提到的rewrite就是这种可以为当前请求打上内部标识的指令,另外像error_page、try_files、index等指令都可以,还有像add_before_body这种用子请求的方式实现功能的指令也是可以的。(关于子请求,后续会有专门文章进行详细介绍)

 

2.5后缀是“/”的location

在nginx中还存在这样一种不太容易被发现的规则,当某个location的后缀是“/”时,该locaiton可以成功匹配一个后缀不是“/”符号的请求。

 

上面这句话读起来好像有点绕,没关系,来看一个实际的例子:

 

location /a/ {

      proxy_pass https://www.baidu.com/;

}

 

从前面学到的知识我们可以知道,上面这个location匹配的uri是以“/a/”开头的请求,而我们最上面那句话“该locaiton可以匹配上一个后缀不是“/”符号的请求”又扩大了这个location的匹配范围,意思是它也可以匹配“/a”这个请求,因为“/a”比配置中的location正好在结尾处少一个“/”。在验证这条规则之前我们先用正常的请求来访问一下看看结果:

 

curl http://127.0.0.1/a/ -v

  

< HTTP/1.1 200 OK

< Content-Type: text/html

< Content-Length: 2443

 

从响应头可以看出他返回了正确的内容,并且内容长度为2443个字节。好,接下来再用“/a”请求一下试试:

 

curl http://127.0.0.1/a -v

 

< HTTP/1.1 301 Moved Permanently

< Content-Type: text/html

< Content-Length: 190

< Location: http://127.0.0.1/a/

 

可以看到,并没有返回404,而是一个301,并且有一个Location响应头。仔细看这个响应头的内容,比我们原请求多了一个“/”符号。去掉scheme和

host部分,单看uri它正好是“/a/”跟我们例子中location配置的uri是一致的,有点类似于nginx自动修正了这个请求,把他导向了一个正确的匹配。

 

针对这条“/”后缀的规则,我们可能会产生另外一种疑问,如果没有“/”后缀的和有“/”这个后缀的location同时存在,那nginx会选择哪个来匹配?就近?随机?不猜了,举几个例子试一下:

 

location /a {

    proxy_pass https://www.jd.com/;

}

 

location /a/ {

    proxy_pass https://www.baidu.com/;

}

 

使用curl模拟“/a”请求,多请求几遍,最后出来的结果总是www.jd.com返回的结果,而用“/a/”请求最终出来的结果总是www.baidu.com返回的结果,即使我们把上面两个配置项上下颠倒,结果总是不变的。所以结论是只有类似“/a”这种配置不存在时,与之对应的“/a/”这种location配置在面对“/a”请求时才会表现出自动修正的动作。

 

另外,细心的读者可能会注意到,之前举例时locaiton里面都是“return”指令,而本节切用的“proxy_pass”指令,难道换个指令会有什么不同?还是用实际例子来看把:

 

location /a/ {

    return 200 “hello”;

}

 

同样的location,不一样的内部指令,先用“/a/”请求访问以下看看结果

 

curl http://127.0.0.1/a/

hello

   

看起来没啥问题,至少证明配置本身没有问题。再用“/a”试试,根据上面的经验,此时应该返回一个301响应码,并且有一个Location头,且内容是“http://127.0.0.1/a/”,究竟是不是呢?来看结果

 

curl http://127.0.0.1/a -v

 

< HTTP/1.1 404 Not Found

< Content-Type: text/html

< Content-Length: 175

 

意思很明显了,代表当前nginx没有一个可以匹配“/a”的location,所以,看到这个结果是不是很困惑?此时你可能会想,之前说的关于有“/”后缀的location规则到底还算不算数?

 

当然算数,只不过要加上一些限制了。在nginx中并不是所有的指令都支持location这种规则,目前在nginx-1.9.4中只有五个指令(最新版又加了一个grpc_pass),分别是proxy_pass,fastcgi_pass,uwsgi_pass,scgi_pass,memcached_pass。 实际上nginx中所有的location都有这种能力,只不过这种能力默认都是关闭的,如果有需要你可以随时把它打开,不过目前不可以通过配置文件的方式打开,只能通过硬编码的方式,而上面这五个指令之所有能支持这种能力,就是因为它们知道这种硬编码规则,并将其打开了。

 

到这里关于“/”后缀的规则基本已经结束了,但是通过前面的讲述我们知道,location的匹配有六种规则,目前我们所有的例子用的都是【无】,那么再刨去一个内部匹配【@】,其它四中是否适用这种“/”后缀规则呢?老规矩,直接上例子:

 

location = /a/ {

    proxy_pass https://www.baidu.com/;

}

 

location ^~ /b/ {

    proxy_pass https://www.baidu.com/;

}

 

location ~* /c/ {

    proxy_pass https://www.baidu.com;

}

 

location ~ /d/ {

    proxy_pass https://www.baidu.com;

}

 

 然后我们分别用“/a”,“/b”,“/c”,“/d”来发起请求,看看会有什么结果:

 

curl http://127.0.0.1/a -v

< HTTP/1.1 301 Moved Permanently

< Location: http://127.0.0.1/a/

 

curl http://127.0.0.1/b -v

< HTTP/1.1 301 Moved Permanently

< Location: http://127.0.0.1/a/

 

curl http://127.0.0.1/c -v

< HTTP/1.1 404 Not Found

  

curl http://127.0.0.1/d -v

< HTTP/1.1 404 Not Found

 

结果一目了然,【~】、【~*】这两种正则模式不支持这种“/”后缀规则,所以最后这种“/”后缀规则的适用范围还要刨除【~】、【~*】这两种模式匹配。

 


3
匹配模式的优先级

 

截止到目前,我们已经介绍完了所有六种匹配模式,如果每次只是使用其中的一种,那么对于看过上面内容的同学来说应该不是什么难事,但如果多种模式一起使用呢?比如某个请求正好处在多个模式的交集匹配范围内,此时应该以哪个模式优先呢?或者另外一个古怪的问题,这几个模式是否可以同时存在呢?

 

针对上面的问题,我们在nginx.conf文件中增加如下配置:

 

1:  location = /a {

      return 200 “[= /a]”;

}

 

2:  location /a {

       return 200 “[/a]”;

}

 

3:  location ^~ /a {

       return 200 “[^~ /a]”;

}

 

4:   location ~ /a {

        return 200 “[~ /a]”;

}

 

5:   location ~* /a {

       return 200 “[~* /a]”;

}

 

为了便于解释,每个location前都加了一个数字编号,另外因为【@】匹配模式是一个内部匹配,所以直接排除掉了他,这样一个配置文件中就有五中匹配模式了。

 

好了,有了上面的这个配置,第一步自然是先启动或是加载nginx,如果你是第一次启动nginx,那么你需要先启动它,我本机安装目录是/my/nginx,所以当进入到安装目录后,我需要做如下操作:

 

$ ./sbin/nginx

 

然后敲回车就可以。如果nginx已经是启动状态,那么直接reload就可以,操作如下:

 

$ ./sbin/nginx -s reload

 

同样回车就可以了。不管是哪种命令,没有消息就是最好的消息,代表启动或reload成功。因为我本机nginx已经启动,所以我直接reload就可以了,来看一下结果:

 

$ ./sbin/nginx -s reload

nginx:[emerg] duplicate location "/a" in /my/nginx/conf/nginx.conf:34

 

遗憾的是居然加载失败(reload)失败,提示信息说有重复的location,因为我们所有的location的uri部分都是“/a”,所以初步怀疑是nginx中不能同时存在同样的location。但是再仔细看这条提示信息,它打印出了错的行数为34,而在我的nginx.conf配置文件中,第34行正好是我们为其编号的第3号location,它的匹配模式是【^~】,所以如果真的是nginx中不能同时存在同样的location,那么是不是在第2号的时候就应该出错呢?有点一头雾水,先把3号location改成下面这样:

 

3:  location ^~ /ab {

       return 200 “^~ /a”;

}

 

然后再reload一次试试:

 

$ ./sbin/nginx -s reload

 

回车后发现可以成功reload,难道【^~】模式无法跟其它模式共存吗?当然不是,这其实是因为【^~】模式跟【无】发生了冲突,因为它俩本来就是同一个东西,只不过匹配完成后,后续的动作不一样了。试想一下,在同一个nginx.conf中有两个同样的location,但是其内部指令且不同,那么当有请求过来的时候,nginx应该如何抉择呢?是按照其在配置文件中的顺序匹配?还是随机匹配呢?还是说在【^~】和【无】中定一个优先级呢?nginx给的答案是,不会有这种存在,在配置阶段就将其视为一种错误存在,这条规则同样适用于【=】匹配,但是并不适用于正则匹配。

    

既然这样,我们就先把3号location放一放,先来看看其它四个location的优先级。先用curl来试一试实际结果:

 

curl http://127.0.0.1/a

[= /a]

 

curl http://127.0.0.1/aa

[~ /a]

 

第一个请求打印出了“[= /a]”应该不会有什么疑问,同样的uri,精确匹配的优先级最高也很合乎常理。而二个请求直接绕过了2号location,并打印出了4号location的结果,这个结果跟我们在2.2节介绍普通匹配时下的结论是吻合的。

 

所以目前的结论大致是这样:

   1.【=】

   2.【^~】

   3.【~】【~*】

   4.【无】

这里面需要注意的是,对于同样的uri,【^~】和【无】只能存在一个,且跟【=】一样,不能重复。但是我们看到,两个正则匹配居然可以重复存在同样的uri,那它们的优先级又是怎样的呢?我们把例子中的4号和5号location的位置互换一下,然后再用同样的请求来试一下结果:

 

curl http://127.0.0.1/a

[= /a]

 

curl http://127.0.0.1/aa

[~* /a]

 

可以看到,第二个请求的输出结果跟之前不一样了,这次是打印出了5号location的结果,所以我们基本可以得出一个结论,正则匹配模式的优先级取决于其在配置文件中的先后顺序。

 

最后,对于这几种模式的优先级,nginx又是如何实现的呢?

 

实际上实现方式也很简单,当nginx最终启动成功后,会有三个容器来存放不同的location,其中:

  • 【=】【无】【^~】在同一个容器(用S表示)中,并且在容器中按字符升序排 序,如果顺序一样则短的在前,比如uri“/abcd”,一定排在uri“/abc”后面。

  • 【~】【~*】两个正则模式在同一个容器(用R表示)中,不过他们并没有按字符升序或长短来排序,同配置文件中的顺序一致。

  • 【@】单独在一个容器中。

除了【@】,当有请求过来的时候,大致规则如下:

 

 

 

  1. nginx先从S容器中按顺序找匹配,如果匹配到一个【=】则终止后续匹配,否则继续。

  2. 如果在S容器中匹配到多个【^~】,则以最后一个为准,并返回匹配成功。

  3. 如果在S容器中匹配到多个【无】,则以最后一个为准,此时会记下这个匹配(记做L1),然后继续从R容器中找匹配。

  4. 如果在R容器中没有找到合适的正则匹配,则终止后续匹配并以L1作为最终匹配。

  5. 如果在R容器中找到一个合适的正则匹配,则终止后续匹配,并以当前匹配作为最终匹配。

  • 大小: 230.9 KB
分享到:
评论

相关推荐

    nginx的server和location匹配规则

    nginx的server和location匹配规则

    nginx location匹配实例详解

    您可能感兴趣的文章:Nginx配置指令location匹配符优先级和安全问题详解Nginx location 匹配规则Nginx服务器的location指令匹配规则详解利用nginx如何匹配多个条件Nginx location匹配规则的方法示例简介Nginx中的...

    详解Nginx location 匹配规则

    语法规则 location [=|~|~*|^~] /uri/ { … } 模式 含义 location = /uri = 表示精确匹配,只有完全匹配上才能生效 location ^~ /uri ^~ 开头对URL路径进行前缀匹配,并且在正则之前。 location ~ ...

    简介Nginx中的location匹配规则

    location匹配命令 ~ #波浪线表示执行一个正则匹配,区分大小写 ~* #表示执行一个正则匹配,不区分大小写 ^~ #^~表示普通字符匹配,如果该选项匹配,只...普通字符匹配,正则表达式规则和长的块规则将被优先和查询

    Nginx location匹配规则的方法示例

    主要介绍了Nginx location匹配规则的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    一文弄懂Nginx的location匹配的实现

    之前对 location 的匹配规则是一知半解的。为了搞明白 location 是如何匹配的,特意花了点时间查了些资料,总结此文。希望能给大家带来帮助。 语法规则 location [ = | ~ | ~* | ^~ ] uri { ... } location @name ...

    Nginx服务器的location指令匹配规则详解

    Location 指令,是用来为匹配的 URI 进行配置,URI 即语法中的”/uri/”,可以是字符串或正则表达式。但如果要使用正则表达式,则必须指定前缀。 nginx location语法 基本语法:location [=|~|~*|^~] /uri/ { … } ...

    Nginx关于location的匹配规则详解.docx

    NULL 博文链接:https://eyesmore.iteye.com/blog/1141660

    Nginx Location 正则_NginxLocation正则.md_

    : 重复0次或1次+ : 重复1次或更多次* : 重复0次或更多次\d :匹配数字^ : 匹配字符串的开始$ : 匹配字符串的介绍{n} : 重复n次{n} : 重复n次或更多次[c] : 匹配单个字符c[a-z] : 匹配a-z小写字母的任意一...

    Nginx服务器中的location配置详解

    语法 location [=|~|~*|^~] /uri/ {…} 规则 = : 表示精确的URI匹配(有兴趣的同学可以看一下url和uri的区别) ...多个location配置的前提下,location的匹配顺序(未验证,嘿嘿,google上搜的) 1.首先匹\u914d=

    详解Nginx之Location配置(Location匹配顺序)

    主要介绍了详解Nginx之Location配置(Location匹配顺序),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    深入理解Nginx中Server和Location的匹配逻辑

    主要介绍了深入理解Nginx中Server和Location的匹配逻辑,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    Nginx Location指令URI匹配规则详解小结

    1、介绍 location指令是http模块当中最核心的一项配置,根据预先定义的URL匹配规则来接收用户发送的请求,根据匹配结果,将请求转发到后台服务器、非法的请求直接拒绝并返回403、404、500...4、location URI匹配规则

    nginx 配置location匹配规则实例讲解

    在本篇文章里小编给大家整理的是关于nginx 配置location匹配规则实例讲解内容,需要的朋友们学习下。

Global site tag (gtag.js) - Google Analytics