V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
NGINX
NGINX Trac
3rd Party Modules
Security Advisories
CHANGES
OpenResty
ngx_lua
Tengine
在线学习资源
NGINX 开发从入门到精通
NGINX Modules
ngx_echo
Hopetree
V2EX  ›  NGINX

nginx 中 location 优先级的问题

  •  
  •   Hopetree ·
    Hopetree · 341 天前 · 2681 次点击
    这是一个创建于 341 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我有一份配置:

    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 个地址为什么会出现不同的匹配生效?

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

    不懂 c ,但是看个大概齐

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

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

    1. 精确匹配 =,如果匹配成功,搜索停止
    2. 前缀匹配,最长位置匹配,如果该匹配具有 ^~,搜索停止
    3. 正则匹配,按配置文件中定义的顺序进行匹配。
    4. 如果第 3 条规则产生匹配的话,结果被使用。否则,使用第 2 条规则的结果。
    xiaooloong
        10
    xiaooloong  
       341 天前
    理论上来说 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
        11
    momooc  
       341 天前 via Android
    不懂,这样看确实,可能有什么点忽略了,坐等大佬发现问题
    coderzhangsan
        12
    coderzhangsan  
       341 天前
    @deepzz 2.前缀匹配,最长位置匹配,如果该匹配具有 ^~,搜索停止; 如何理解这个最长位置?
    google2020
        13
    google2020  
       341 天前
    人生苦短,我选 openresty
    rockyliang
        14
    rockyliang  
       341 天前   ❤️ 2
    @coderzhangsan #12 ,其实就是匹配相同的子字符串,看哪个子字符串最长

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

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

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

    而 405 没有阻止继续匹配正则,所以会继续匹配正则模式,而正则模式就是 402 那个,所以会返回 402
    wscgogo
        20
    wscgogo  
       341 天前
    TommySung
        21
    TommySung  
       341 天前
    是没问题的!

    毫无疑问,匹配原则是精确匹配,即最大程度能匹配上

    在这一原则下,最长匹配就是判断标准

    1.
    首先 /static/js/css/4.png 会匹配到 1 ,保留
    然后,/static/js/css/4.png 会匹配到 3 ,而 4 ,5 ,6 都无法与其”匹敌“,因为 3 和他们比较匹配度最高
    而 1 因为是正则匹配,且能匹配到结尾扩展名,也属于匹配度最高,换句话说你使用/static/js/css/4.jpg 则会匹配到 3

    在此情况下,1 和 3 匹配度都最高,那么正则优先,所以 /static/js/css/4.png 会匹配到 1


    2.
    /static/js/4.png 最高匹配度会匹配到 1 ,3 ,5 ,但 5 的优先级明显高于 1 和 3 ,所以返回 5

    3.
    /static/js/css6/4.png 同上

    容易错误的地方如:
    location /static/js/css/
    location ^~ /static/js/
    如果你的 uri 包括/static/js/css/ ,那么虽然 location ^~ /static/js/ 也能匹配上,并且优先级高于 location /static/js/css/
    但 location /static/js/css/ 属于最精确匹配或最高匹配度,最后就会被选中

    匹配度最高或最精准匹配 是 大于 所谓优先级的
    godall
        22
    godall  
       341 天前
    @rockyliang 大师
    Leon406
        23
    Leon406  
       341 天前

    凑个热闹
    Hopetree
        24
    Hopetree  
    OP
       341 天前
    @Leon406 怎么解释这个图,有相关文章说明的吗,有的话麻烦推一下,我看了好多文章,但是把我测试的配置和请求结果拿去验证发现没有有个经得住验证的
    omgr
        25
    omgr  
       341 天前
    官方文档写的很清楚了,细读下就好了。

    > If the longest matching prefix location has the “^~” modifier then regular expressions are not checked

    你给的测试网址也说明了这个问题,prefix 匹配实际上分两种,如果最长匹配命中的是 ^~ 类型的,就会直接返回,如果是普通的,同时有正则的话,就会按写的顺序依次正则匹配,正则匹配到了就用正则了。

    你的例子中:
    /static/js/css/4.png 最长前缀匹配到了 /static/js/css/ 会继续检查正则
    /static/js/4.png 最长前缀匹配到了 ^~ /static/js/ 直接返回
    omgr
        26
    omgr  
       341 天前
    跟 405 状态码没有关系的
    Hopetree
        27
    Hopetree  
    OP
       341 天前
    经过上面 21 和 25 楼的解析,我画了一个流程图,不知道按照验证好像是对的,请各位大佬看看,是不是这样
    ![image]( https://github.com/Hopetree/izone/assets/30201215/fa522cfe-9e5a-453b-b2f6-2f89b9250596)
    Hopetree
        28
    Hopetree  
    OP
       341 天前
    重新编辑一下:经过上面 21 和 25 楼的解析,我画了一个流程图,按照验证好像是对的,请各位大佬看看,是不是这样

    ![image]( https://github.com/Hopetree/izone/assets/30201215/fa522cfe-9e5a-453b-b2f6-2f89b9250596)
    Hopetree
        30
    Hopetree  
    OP
       336 天前
    我为这个讨论的结果写了一篇学习总结文章,有兴趣的可以看看,https://tendcode.com/subject/article/nginx-location/,如果文章中表述有错误的,欢迎指出纠正
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3677 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 04:25 · PVG 12:25 · LAX 20:25 · JFK 23:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.