AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / server / 问题 / 1168303
Accepted
Ivan Shatsky
Ivan Shatsky
Asked: 2024-11-29 19:18:42 +0800 CST2024-11-29 19:18:42 +0800 CST 2024-11-29 19:18:42 +0800 CST

真正的nginx位置选择算法是什么样的?

  • 772

有这个简单的 nginx 配置:

location / {
    return 200 "Location 1\n";
}
location ~ \.php$ {
    return 200 "Location 2\n";
}
location /tmp {
    return 200 "Location 3\n";
    location ~ \.php$ {
        return 200 "Location 3a\n";
    }
}

为什么/tmp/foo.php请求会给出Location 3a响应,尽管根据文档,除非前缀位置有修饰符,否则正则表达式位置Location 2应该超过请求?Location 3^~

nginx
  • 1 1 个回答
  • 71 Views

1 个回答

  • Voted
  1. Best Answer
    Ivan Shatsky
    2024-11-29T19:18:42+08:002024-11-29T19:18:42+08:00

    发生这种情况的原因是 nginx 中实际选择位置的算法与文档中描述的不同。或者更具体地说,官方文档没有解释嵌套位置的位置选择过程,而这要复杂得多。到目前为止,我还没有遇到任何英文文章解释它是如何真正工作的,所以我在这里尝试澄清一下。

    让我们从原始问题中提供的配置开始:

    location / {
        return 200 "Location 1\n";
    }
    location ~ \.php$ {
        return 200 "Location 2\n";
    }
    location /tmp {
        return 200 "Location 3\n";
        location ~ \.php$ {
            return 200 "Location 3a\n";
        }
    }
    

    人们可以认为请求/tmp/foo.php将继续进行location ~ \.php$ { ... }(标记为“位置 2”),因为文档明确指出:

    为了找到与给定请求匹配的位置,nginx 首先检查使用前缀字符串(前缀位置)定义的位置。从中,选择并记住具有最长匹配前缀的位置。然后按照正则表达式在配置文件中出现的顺序检查正则表达式。正则表达式的搜索在第一次匹配时终止,并使用相应的配置。

    我们来检查一下:

    > curl http://127.0.0.1/tmp/foo.php
    Location 3a
    

    出乎意料,不是吗?文档没有说明的是,在确定最长匹配前缀位置后,nginx 将开始对其嵌套位置(如果有)进行新的搜索迭代。此过程以递归方式继续:在每个步骤中,都应用相同的匹配规则,并且如果找到匹配的嵌套正则表达式位置,而没有匹配的嵌套最长前缀位置,则 nginx 停止进一步搜索并使用该位置来处理请求。

    在我们的示例中,对于/tmp/foo.php请求:

    1. Nginx 首先识别最长匹配的前缀位置location /tmp { ... }(标记为“位置 3”)。
    2. 新的搜索迭代在其嵌套位置开始。
    3. 嵌套location ~ \.php$ { ... }(标记为“位置 3a”)匹配并用于处理请求。

    现在,让我们通过添加第四个位置来扩展配置:

    location /tmp/foo {
        return 200 "Location 4\n";
    }
    

    测试相同请求:

    > curl http://127.0.0.1/tmp/foo.php
    Location 2
    

    这里发生了什么?

    1. 第一步,最长匹配前缀位置是location /tmp/foo { ... }。
    2. 由于没有嵌套位置,nginx 在同一级别评估正则表达式位置。
    3. 正则表达式位置location ~ \.php$ { ... }匹配并用于处理请求。

    其他请求/tmp/bar.php继续按以前的方式处理:

    > curl http://127.0.0.1/tmp/bar.php
    Location 3a
    

    接下来,让我们再次修改配置,将location /tmp/foo { ... }里面的移动到location /tmp { ... }:

    location / {
        return 200 "Location 1\n";
    }
    location ~ \.php$ {
        return 200 "Location 2\n";
    }
    location /tmp {
        return 200 "Location 3\n";
        location ~ \.php$ {
            return 200 "Location 3a\n";
        }
        location /tmp/foo {
            return 200 "Location 3b\n";
        }
    }
    

    运行一些测试:

    > curl http://127.0.0.1/tmp/foo.php
    Location 3a
    > curl http://127.0.0.1/tmp/foo.html
    Location 3b
    

    到目前为止还没有什么意外。

    现在,让我们检查一下^~location 指令修饰符。根据文档:

    如果最长匹配的前缀位置具有^~修饰符,则不检查正则表达式。

    让我们使用以下配置来检查一下:

    location / {
        return 200 "Location 1\n";
    }
    location ~ \.php$ {
        return 200 "Location 2\n";
    }
    location /tmp {
        return 200 "Location 3\n";
        location ~ \.php$ {
            return 200 "Location 3a\n";
        }
    }
    location ^~ /tmp/foo {
        return 200 "Location 4\n";
    }
    

    运行一些测试:

    > curl http://127.0.0.1/tmp/foo.php
    Location 4
    > curl http://127.0.0.1/tmp/foo.html
    Location 4
    > curl http://127.0.0.1/tmp/bar.php
    Location 3a
    > curl http://127.0.0.1/tmp/bar.html
    Location 3
    

    似乎一切都按预期进行。^~修饰符 onlocation ^~ /tmp/foo { ... }确保同一级别的正则表达式位置(例如location ~ \.php$ { ... })不会超过 之类的匹配请求/tmp/foo.php。

    现在,有件事可能会让你大吃一惊。让我们修改一下配置,再次移动location ^~ /tmp/foo { ... }内部location /tmp { ... }:

    location / {
        return 200 "Location 1\n";
    }
    location ~ \.php$ {
        return 200 "Location 2\n";
    }
    location /tmp {
        return 200 "Location 3\n";
        location ~ \.php$ {
            return 200 "Location 3a\n";
        }
        location ^~ /tmp/foo {
            return 200 "Location 3b\n";
        }
    }
    

    让我们测试相同的/tmp/foo.php请求。您期望响应是Location 3b,对吗?

    > curl http://127.0.0.1/tmp/foo.php
    Location 2
    

    哎呀……看起来很奇怪,不是吗?好吧,这可能是需要解释的算法的最后一个方面。在确定最嵌套级别(在我们的例子中是,location ^~ /tmp/foo { ... }标记为“位置 3b”)的最长匹配前缀位置后,如果在其中找不到匹配的正则表达式位置,则 nginx 会记住该位置并开始沿配置树向上追溯到最外层。在上升过程中经过的每个级别,都会采取以下操作:

    • 如果当前级别上最长匹配的前缀位置没有修饰符^~,nginx 会将请求与该级别定义的所有正则表达式位置进行匹配。第一个匹配的正则表达式位置(如果有)用于处理请求;否则,nginx 继续升序。
    • 如果当前层级上的最长匹配前缀位置带有修饰符^~,nginx 将跳过与该层级上定义的正则表达式位置匹配请求,并立即上升到下一层级。如果已经到达最外层,则使用记住的最嵌套层级中的最长匹配前缀位置来处理请求。

    就是这样。^~如果最外层最长匹配前缀位置(位置 3)没有修饰符,则在嵌套前缀位置(位置 3b)上使用修饰符并不能保证请求不会被外部正则表达式位置(位置 2)超越^~。

    PS 当然,需要指出的是,如果=在下降过程中的任何级别找到完全匹配的位置(带有修饰符的位置),nginx 将立即停止搜索,并使用找到的位置来处理请求。考虑到这一点,典型的 nginx 配置如下

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/path/to/php-fpm/socket;
    }
    

    index.php如果文件是请求的唯一处理程序,则可以进行显著优化,这在许多用例中很常见:

    location = /index.php {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/path/to/php-fpm/socket;
    }
    
    • 3

相关问题

  • Gzip 与反向代理缓存

  • nginx 作为代理的行为

  • Nginx 学习资源 [关闭]

  • 提供 70,000 个静态文件 (jpg) 的最佳方式?

  • 在 Apache、LightTPD 和 Nginx Web 服务器上提供 PHP 5.x 应用程序的现状?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    新安装后 postgres 的默认超级用户用户名/密码是什么?

    • 5 个回答
  • Marko Smith

    SFTP 使用什么端口?

    • 6 个回答
  • Marko Smith

    命令行列出 Windows Active Directory 组中的用户?

    • 9 个回答
  • Marko Smith

    什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同?

    • 3 个回答
  • Marko Smith

    如何确定bash变量是否为空?

    • 15 个回答
  • Martin Hope
    Tom Feiner 如何按大小对 du -h 输出进行排序 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich 什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent 如何确定bash变量是否为空? 2009-05-13 09:54:48 +0800 CST
  • Martin Hope
    cletus 您如何找到在 Windows 中打开文件的进程? 2009-05-01 16:47:16 +0800 CST

热门标签

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve