nginx 中 location 优先级的问题

188 天前
 Hopetree

我有一份配置:

server {
    listen 12080;
    server_name abc.com;

    access_log  /var/log/nginx/test.access.log;
    error_log   /var/log/nginx/test.error.log;

    location ~* \.png$ {
        return 402;
    }

    location / {
        return 400;
    }

    location /static/js/css/ {
        return 405;
    }

    location ^~ /static/ {
        return 401;
    }

    location ^~ /static/js/ {
        return 404;
    }

    location = /static/abc.png {
        return 403;
    }
}

为什么/static/js/css/4.png 返回 402 ,而/static/js/4.png 返回 404 ?正则的优先级不是很低吗,第一个为什么是正则生效,并且/static/js/css6/4.png 又是 404 ,第一个地址和第 3 个地址为什么会出现不同的匹配生效?

2431 次点击
所在节点    NGINX
30 条回复
sundong
188 天前
`=` 开头表示精确匹配 ,如 A 中只匹配根目录结尾的请求,后面不能带任何字符串。
`^~` 开头表示 uri 以某个常规字符串开头,不是正则匹配
`~` 开头表示区分大小写的正则匹配;
`~*` 开头表示不区分大小写的正则匹配
`/` 通用匹配, 如果没有其它匹配,任何请求都会匹配到
Hopetree
188 天前
@sundong /static/js/css/4.png 按照^~优先不是应该匹配到 404 那条吗,怎么会到 402 的正则,然后/static/js/css6/4.png 又匹配的是正则,这两个的区别是啥导致的?
Huelse
188 天前
= > ^~ > ~/~* > 空格
dzdh
188 天前
https://github.com/nginx/nginx/blob/master/src/http/ngx_http_core_module.c#L3087

不懂 c ,但是看个大概齐

先判断 location 参数? 3 个优先?三个参数的时候,优先级是 =、^~、~、~*、

else 不是三个参数,优先级:=、^~、~(如果还有后续参数*?)
Hopetree
188 天前
@Huelse 按照这个顺序的话,/static/js/css/4.png 不是应该先被 404 那个规则命中吗,怎么最终的结果是 402 ?
dzdh
188 天前
dzdh
188 天前
@dzdh #4 这个跟优先级貌似没关系就是挨个匹配处理添加到 locations 里
Huelse
188 天前
@Hopetree #5 因为 402 那个是最长匹配
deepzz
188 天前
可以看看匹配的规则: https://deepzz.com/post/how-to-write-nginx-server.html#toc_6

1. 精确匹配 =,如果匹配成功,搜索停止
2. 前缀匹配,最长位置匹配,如果该匹配具有 ^~,搜索停止
3. 正则匹配,按配置文件中定义的顺序进行匹配。
4. 如果第 3 条规则产生匹配的话,结果被使用。否则,使用第 2 条规则的结果。
xiaooloong
188 天前
理论上来说 9 楼说的对,但试了一下

```bash
root@ubuntu:/usr/local/openresty/nginx/conf# curl -I localhost:12080/foobar
HTTP/1.1 400 Bad Request
Server: openresty/1.21.4.2
Date: Thu, 21 Dec 2023 06:25:54 GMT
Content-Type: text/html
Content-Length: 163
Connection: close

root@ubuntu:/usr/local/openresty/nginx/conf# curl -I localhost:12080/static/js/css/4.png
HTTP/1.1 402 Payment Required
Server: openresty/1.21.4.2
Date: Thu, 21 Dec 2023 06:26:04 GMT
Content-Type: text/html
Content-Length: 173
Connection: keep-alive

root@ubuntu:/usr/local/openresty/nginx/conf# curl -I localhost:12080/static/js/4.png
HTTP/1.1 404 Not Found
Server: openresty/1.21.4.2
Date: Thu, 21 Dec 2023 06:26:16 GMT
Content-Type: text/html
Content-Length: 159
Connection: keep-alive

root@ubuntu:/usr/local/openresty/nginx/conf# curl -I localhost:12080/static/js/css6/4.png
HTTP/1.1 404 Not Found
Server: openresty/1.21.4.2
Date: Thu, 21 Dec 2023 06:26:40 GMT
Content-Type: text/html
Content-Length: 159
Connection: keep-alive
```

确实有这个问题
momooc
187 天前
不懂,这样看确实,可能有什么点忽略了,坐等大佬发现问题
coderzhangsan
187 天前
@deepzz 2.前缀匹配,最长位置匹配,如果该匹配具有 ^~,搜索停止; 如何理解这个最长位置?
google2020
187 天前
人生苦短,我选 openresty
rockyliang
187 天前
@coderzhangsan #12 ,其实就是匹配相同的子字符串,看哪个子字符串最长

比如有三个 location :
A:/a
B: /a/b
C:/a/b/c

对于字符串 /a/b/c/d ,A 、B 、C 三个都能匹配到,但 C 是最长的,所以最终会采用 C 这个 location
Hopetree
187 天前
@deepzz 其实之前我也看了匹配规则的,就是因为看了然后验证发现不对,比如按照你这个逻辑,无法解释为什么/static/js/css/4.png 返回 402 ,/static/js/css6/4.png 又是 404 这两个明明都是同时匹配到了前缀和正则,为什么结果不同
Hopetree
187 天前
给大家提供一个网站可以很快的验证匹配规则,不用自己去配置 nginx ,网站地址: https://nginx.viraptor.info/
Hopetree
187 天前
@rockyliang 按照这个说法,/static/js/css/4.png 已经匹配到了前缀/static/js/,为什么没有停止搜索,最终是正则生效了?而/static/js/css6/4.png 又是前缀/static/js/来生效的?这个说不通
liangxiangdong
187 天前
我之前遇到过这个问题,就是正则和前缀匹配都满足的情况,正则优先。除非前缀匹配有修饰符,不过具体什么修饰符可以走到 405 我还真没研究过。https://docs.nginx.com/nginx/admin-guide/web-server/web-server/ /some/path/document.html 下面有一段话。
rockyliang
187 天前
@Hopetree #17

因为 405 、401 、404 这三个都属于前缀模式匹配。/static/js/css/4.png 同时匹配到了这三个前缀模式,当同时匹配到多个前缀模式时,需要按最长匹配规则进行选取,即最终会命中 405 。

而 405 没有阻止继续匹配正则,所以会继续匹配正则模式,而正则模式就是 402 那个,所以会返回 402
wscgogo
187 天前

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1002226

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX