if 条件破坏了 nginx 配置中的 try_files

问题描述 投票:0回答:2

我的 nginx 配置中有一个简单的

location
块,它与我的网站的静态文件匹配。我想做的是使用
try_files
检查文件是否存在,如果不存在,则重定向到 URL(在本例中在
@cdn
位置块中指定)。我还想设置一些 CORS 标头。

以下是相关配置。

location ~* \.(css|js|jpe?g|png|gif|otf|eot|svg|ttf|woff|woff2|xml|json)$ {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' "$http_origin";
        add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;

        return 204;
    }

    if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' "$http_origin";
        add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
    }

    if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' "$http_origin";
        add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
    }

    try_files $uri @cdn;
}

location @cdn {
    return 301 https://example.com$request_uri;
}

问题是,如果文件不存在,我会收到 404 响应,而不是 301 重定向。在添加 CORS 标头之前,配置工作正常。如果我删除标头的处理,一切都会按预期进行,并且我会收到 301 响应。

现在我已经阅读了一些关于为什么 if 指令不好并且应该避免的内容,但我仍然不知道为什么它会破坏我的配置。如果我理解正确的话,它与

if
add_header
作为重写模块或类似内容的一部分有关,我猜这与
try_files
冲突。也许我在这里不准确,但无论如何我不知道如何解决它。

为什么当找不到文件时,

if
和/或
add_header
的存在会让nginx给我一个404而不是301,我该如何修复它?预先感谢!

nginx
2个回答
11
投票

http://agentzh.blogspot.co.uk/2011/03/how-nginx-location-if-works.html您可能会对了解

if
如何工作感兴趣。在您的情况下,当
if
条件匹配时,请求现在在
if
上下文中提供服务,并且
try_files
不会由该上下文继承。或者正如 https://www.digitalocean.com/community/tutorials/understanding-the-nginx-configuration-file-struct-and-configuration-contexts 所说“使用 if 上下文时要记住的另一件事是它使同一上下文中的 try_files 指令变得无用。”

此外,如果

try_files
回落到
@cdn
,那么您之前添加的任何标头都会被忘记,它会在新的
location
块中再次开始,因此需要在此处添加标头。

至于如何修复;您可以在

if
中设置变量,并且
add_header
会忽略空值,因此这样的操作应该有效:

set $access-control-output 0;
location ~* \.(css|js|jpe?g|png|gif|otf|eot|svg|ttf|woff|woff2|xml|json)$ {
    set $access-control-output 1;
    try_files $uri @cdn;
}

set $acao = "";
set $acam = "";
if ($access-control-output) {
    set $acao = $http_origin;
    set $acam = "GET, OPTIONS";
}

map "$access-control-output:$request_method" $acma {
    "1:OPTIONS" 1728000; 
    default     "";
}

location @cdn {
    add_header 'Access-Control-Allow-Origin' $acao;
    add_header 'Access-Control-Allow-Methods' $acam;
    add_header 'Access-Control-Max-Age' $acma;
    return 301 https://example.com$request_uri;
}

编辑:您不关心@cdn后备中的标头,在这种情况下您应该能够拥有如下内容:

map $request_method $acma {
    "OPTIONS" 1728000; 
    default   "";
}

location ~* \.(css|js|jpe?g|png|gif|otf|eot|svg|ttf|woff|woff2|xml|json)$ {
    add_header 'Access-Control-Allow-Origin' $http_origin;
    add_header 'Access-Control-Allow-Methods' "GET, OPTIONS";
    add_header 'Access-Control-Max-Age' $acma;
    try_files $uri @cdn;
}

location @cdn {
    return 301 https://example.com$request_uri;
}

0
投票

使用Nginx多年后,我确实遇到过这个问题,并且发现很少有讨论,我认为大多数开发人员都没有意识到

if
条件与
try_files
冲突...

无论如何,@M Somerville 出色的解释和解决方案的另一个选择可能是简单地反转你的

if
语句的逻辑,如下所示:

https://serverfault.com/a/579739/144798

在该线程/答案中,OP正在使用

if ($args ~ "api_url") {
,建议是像
 if ($arg_api_url != '') {
一样反转,以避免这些请求陷入
if
的黑洞......

© www.soinside.com 2019 - 2024. All rights reserved.