nginx try_files 处理两次,如果设置了错误回退,则会失败

nginx try_files 处理两次,如果设置了错误回退,则会失败

我有一个位置块设置来捕获所有文件请求并将它们发送到PHP-FPM

location  / {
    try_files  $uri /routing.php?$args;
    fastcgi_pass   unix:/opt/local/var/run/php54/php-fpm-www.sock;
    include       /documents/projects/intahwebz/intahwebz/conf/fastcgi.conf;
}

这有效并且正确地将请求传递给PHP-FPM所请求的确切现有 php 文件或routing.php设置为要运行的脚本。

我尝试添加一个错误页面,这样如果路由文件被删除或者不可用,就会显示错误页面,而不是 Nginx 的默认错误页面:

location  / {
    try_files $uri /routing.php?$args /50x_static.html;
    fastcgi_pass   unix:/opt/local/var/run/php54/php-fpm-www.sock;
    include       /documents/projects/intahwebz/intahwebz/conf/fastcgi.conf;
}

这将停止routing.php提供文件,并显示 50x_static.html 页面。对现有 PHP 文件的请求仍然有效,即转到 URL/dynamic.php

我意识到try_files命令中的最后一个参数有点神奇:

如果找不到文件,则会调用内部重定向到最后一个参数。请注意,只有最后一个参数会导致内部重定向,前一个参数只会设置内部 URI 指针。最后一个参数是后备 URI,必须存在,否则将引发内部错误。

在调查 error_page 为何破坏配置时,我意识到对于有效的配置(没有静态错误页面),根据 Nginx 重写日志,Nginx 在尝试获取根 URL“/”时似乎匹配了两次请求:

"^/proxy/(\d+)/(\w+)/(.+)\.(gif|png|jpg|jpeg|GIF|PNG|JPG|JPEG)$" does not match "/", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/proxy/(\d+)/(.+)\.(gif|png|jpg|jpeg|GIF|PNG|JPG|JPEG)$" does not match "/", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/staticImage/(\w+)/(.+)\.([^\.]*)$" does not match "/", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/proxy/(\d+)/(\w+)/(.+)\.(gif|png|jpg|jpeg|GIF|PNG|JPG|JPEG)$" does not match "/routing.php", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/proxy/(\d+)/(.+)\.(gif|png|jpg|jpeg|GIF|PNG|JPG|JPEG)$" does not match "/routing.php", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"
"^/staticImage/(\w+)/(.+)\.([^\.]*)$" does not match "/routing.php", client: 127.0.0.1, server: basereality.com, request: "GET / HTTP/1.1", host: "basereality.test"

即,请求以 的形式传入/try_files无法提供文件,因此将请求从 重写//routing.php,然后重新处理该请求。

为什么尝试文件routing.php在第一次传递时不提供文件?该文件存在并且可以访问,否则第二次传递时就不会提供该文件。

编辑

删除了不相关的配置。

答案1

您引用的文档明确指出“调用了对最后一个参数的内部重定向”。内部重定向的处理方式与来自客户端的初始请求相同 - 这包括rewrite在服务器级别处理语句,您可以在日志中看到这些语句。但是,如果除最后一个参数之外的任何其他参数与现有文件匹配,则将使用语句所在的配置try_files来处理请求,并且不会有第二次匹配。locationtry_files

至于您的规则,您是否尝试过$args省略try_files

location  / {
    try_files $uri /routing.php /50x_static.html;
    fastcgi_pass   unix:/opt/local/var/run/php54/php-fpm-www.sock;
    include       /documents/projects/intahwebz/intahwebz/conf/fastcgi.conf;
}

请注意,$uri不包含$args太多;查询参数仍将通过参数传递给 FastCGI 后端QUERY_STRING,该参数大概在您的中设置fastcgi.conf

fastcgi_param QUERY_STRING    $query_string;

如果文件不$uri存在/routing.php,则请求将被重定向到配置中的部分/50x_static.html并根据该location = /50x_static.html部分进行处理(但仍将执行第二次重写尝试,因为重写规则放在服务器级别)。

您的配置中有一个非常可疑的细节,即无论文件扩展名是什么,您都通过 PHP 传递所有文件 - 这是非常不寻常的,并且可能会由于在非预期的文件中执行 PHP 代码而导致安全问题。

答案2

Sergey 的说法是正确的;$args在 try_files 命令中添加内容会阻止文件匹配,因此会将 Nginx 送入另一个处理循环,这很奇怪,因为这就是 Nginx例如说要做

如果你严格按照这个例子来做:

try_files $uri $uri/ /index.php?q=$uri&$args;

如果$uri$uri/不存在,Nginx 将转到最后一个神奇参数。它甚至不会尝试匹配文件,而是重新处理请求,将新路径设置为 ,现在的/index.php和设置为。$argsq=$uri&$args

另一方面,如果你在try_filesindex.php 之后有另一个值,例如

try_files $uri $uri/ /index.php?q=$uri&$args /50x_static.html;

Nginx 查找一个名为的文件/index.php?q=$uri&$args,但显然它不存在,因此它转到/50x_static.html并重新处理该文件。

因为$uriNginx 重新处理请求时会重写,所以要避免这种情况需要进行一些细微的更改。如果您将配置设置为如下形式:

set $originalURI  $uri;
try_files $uri $uri/ /index.php  /50x_static.html;
fastcgi_param  QUERY_STRING  q=$originalURI&$query_string;

Nginx 查找的文件是/index.php正确名称的文件,因此 nginx 将停止处理。我们必须在修改$uri变量时复制它,因此如果我们在 try_files 之后使用它,try_files那就公平了。/index.php

相关内容