nginx的location匹配实战

  • location匹配的规则

请参考 Nginx的location匹配规则

  • location 匹配练习

1.先普通 location ,再正则 location

周边不少童鞋告诉我, nginx 是“先匹配正则 location 再匹配普通 location ”,其实这是一个误区, nginx 其实是“先匹配普通 location ,再匹配正则 location ”,但是普通 location 的匹配结果又分两种:一种是“严格精确匹配”,官方英文说法是“ exact match ”;另一种是“最大前缀匹配”,官方英文说法是“ Literal strings match the beginning portion of the query – the most specific match will be used. ”。我们做个实验:

例1:

server {
       listen       9090;
       server_name  localhost;
       location / {
           root   html;
           index  index.html index.htm;
           deny all;
       }
       location ~ \.html$ {
           allow all;
       }
}

附录 nginx 的目录结构是: nginx->html->index.html

上述配置的意思是:

location / {… deny all;} 普通 location 以“ / ”开始的 URI 请求(注意任何 HTTP 请求都必然以“/ ”开始,所以“ / ”的意思是所有的请求都能被匹配上),都拒绝访问;

location ~.html$ {allow all;} 正则 location以 .html 结尾的 URI 请求,都允许访问。

测试请求

测试结果

测试结果详解:

curl http://localhost:9090/arrow-up-right 的结果是“ 403 Forbidden ”,说明被匹配到“ location / {..deny all;} ”了,原因很简单HTTP 请求 GET / 被“严格精确”匹配到了普通 location / {} ,则会停止搜索正则 location ;

curl http://localhost:9090/index.htmlarrow-up-right 结果是“ Welcome to nginx! ”,说明没有被“ location / {…deny all;} ”匹配,否则会 403 Forbidden ,但 /index.html 的确也是以“ / ”开头的,只不过此时的普通 location / 的匹配结果是“最大前缀”匹配,所以 Nginx 会继续搜索正则 location , location ~ .html$ 表达了以 .html 结尾的都 allow all; 于是接着就访问到了实际存在的 index.html 页面。

curl http://localhost:9090/index_notfound.htmlarrow-up-right 同样的道理先匹配 location / {} ,但属于“普通 location 的最大前缀匹配”,于是后面被“正则 location ” location ~ .html$ {} 覆盖了,最终 allow all ; 但的确目录下不存在index_notfound.html 页面,于是 404 Not Found 。

curl http://localhost:9090/index.txtarrow-up-right 因为先匹配上了 location / {..deny all;} 尽管属于“普通 location ”的最大前缀匹配结果,继续搜索正则 location ,但是 /index.txt 不是以 .html结尾的,正则 location 失败,最终采纳普通 location 的最大前缀匹配结果,于是 deny all 了。

2. 普通 location 的“隐式”严格匹配

例题 2 我们在例题 1 的基础上增加精确配置

测试请求:

测试结果

结果进一步验证了“普通 location ”的“严格精确”匹配会终止对正则 location 的搜索。这里我们小结下“普通 location”与“正则 location ”的匹配规则:先匹配普通 location ,再匹配正则 location ,但是如果普通 location 的匹配结果恰好是“严格精确( exact match )”的,则 nginx 不再尝试后面的正则 location ;如果普通 location 的匹配结果是“最大前缀”,则正则 location 的匹配覆盖普通 location 的匹配。也就是前面说的“正则 location 让步普通location 的严格精确匹配结果,但覆盖普通 location 的最大前缀匹配结果”。

3. 普通 location 的“显式”严格匹配和“ ^~ ” 前缀

上面我们演示的普通 location 都是不加任何前缀的,其实普通 location 也可以加前缀:“ ^~ ”和“ = ”。其中“ ^~”的意思是“非正则,不需要继续正则匹配”,也就是通常我们的普通 location ,还会继续搜索正则 location (恰好严格精确匹配除外),但是 nginx 很人性化允许配置人员告诉 nginx 某条普通 location ,无论最大前缀匹配,还是严格精确匹配都终止继续搜索正则 location ;而“ = ”则表达的是普通 location 不允许“最大前缀”匹配结果,必须严格等于,严格精确匹配。

例题 3 :“ ^~ ”前缀的使用

把例题 2 中的 location / {} 修改成 location ^~ / {} ,再看看测试结果:

URI 请求

修改前

修改后

403 Forbidden

403 Forbidden

Welcome to nginx!

403 Forbidden

除了 GET /exact/match.html 是 404 Not Found ,其余都是 403 Forbidden ,原因很简单所有请求都是以“ / ”开头,所以所有请求都能匹配上“ / ”普通 location ,但普通 location 的匹配原则是“最大前缀”,所以只有/exact/match.html 匹配到 location /exact/match.html {allow all;} ,其余都 location ^~ / {deny all;} 并终止正则搜索。

例题 4 :“ = ”前缀的使用

例题 4 相对例题 2 把 location / {} 修改成了 location = / {} ,再次测试结果:

URI 请求

修改前

修改后

403 Forbidden

403 Forbidden

Welcome to nginx!

Welcome to nginx!

403 Forbidden

404 Not Found

最能说明问题的测试是 GET /test.jsp ,实际上 /test.jsp 没有匹配正则 location ( location ~.html$ ),也没有匹配 location = / {} ,如果按照 location / {} 的话,会“最大前缀”匹配到普通 location / {} ,结果是 deny all 。

4. 正则 location 与编辑顺序

location 的指令与编辑顺序无关,这句话不全对。对于普通 location 指令,匹配规则是:最大前缀匹配(与顺序无关),如果恰好是严格精确匹配结果或者加有前缀“ ^~ ”或“ = ”(符号“ = ”只能严格匹配,不能前缀匹配),则停止搜索正则 location ;但对于正则 location 的匹配规则是:按编辑顺序逐个匹配(与顺序有关),只要匹配上,就立即停止后面的搜索。

配置 3.1

配置 3.2

测试结果:

URI 请求

配置 3.1

配置 3.2

解释:

Location ~ ^/prefix/.*.html$ {deny all;} 表示正则 location 对于以 /prefix/ 开头, .html 结尾的所有 URI 请求,都拒绝访问;

location ~.html${allow all;} 表示正则 location 对于以 .html 结尾的 URI 请求,都允许访问。 实际上,prefix 的是 ~.html$ 的子集。

在“配置 3.1 ”下,两个请求都匹配上 location ~.html$ {allow all;} ,并且停止后面的搜索,于是都允许访问, 404 Not Found ;在“配置 3.2 ”下, /regextest.html 无法匹配 prefix ,于是继续搜索 ~.html$ ,允许访问,于是 404 Not Found ;然而 /prefix/regextest.html 匹配到 prefix ,于是 deny all , 403 Forbidden 。

配置 3.3

配置 3.4

测试结果:

URI 请求

配置 3.1

配置 3.2

测试结果表明:普通 location 的匹配规则是“最大前缀”匹配,而且与编辑顺序无关。

#5 “@” 前缀 Named Location 使用

REFER: http://nginx.org/en/docs/http/ngx_http_core_module.html#error_pagearrow-up-right

假设配置如下:

上述配置文件的意思是:如果请求的 URI 存在,则本 nginx 返回对应的页面;如果不存在,则把请求代理到baidu.com 上去做个弥补(注: nginx 当发现 URI 对应的页面不存在, HTTP_StatusCode 会是 404 ,此时error_page 404 指令能捕获它)。

测试一:

当我们 GET /nofound.html 发送给本 nginx , nginx 找不到对应的页面,于是 error_page 404 = @fallback ,请求被代理到

http://www.baidu.comarrow-up-right,于是 nginx 给 http://www.baidu.comarrow-up-right 发送了 GET /nofound.html ,但/nofound.html 页面在百度也不存在,百度 302 跳转到错误页。

直接访问 http://www.baidu.com/nofound.htmlarrow-up-right

结果:

测试二:访问一个 nginx 不存在,但 baidu 存在的页面

当 curl http://localhost:9090/duty/arrow-up-right -i 时, nginx 没找到对应的页面,于是 error_page = @fallback ,把请求代理到 baidu.com 。注意这里的 error_page = @fallback 不是靠重定向实现的,而是所说的“ internally redirected (forward )”。

Last updated