【问题标题】:if conditions break try_files in nginx configuration如果条件破坏了 nginx 配置中的 try_files
【发布时间】:2016-09-17 15:04:44
【问题描述】:

我的 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 指令不好并且应该避免使用的内容,但我仍然不知道它为什么会破坏我的配置。如果我理解正确,它与ifadd_header 是重写模块的一部分或类似的东西有关,我猜这与try_files 冲突。也许我在这里不准确,但无论哪种方式我都不确定如何解决它。

为什么在找不到文件时if 和/或add_header 的存在使nginx 给我一个404 而不是301,我该如何解决它?提前致谢!

【问题讨论】:

    标签: nginx


    【解决方案1】:

    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-structure-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;
    }
    

    【讨论】:

    • 非常感谢您的回答!这种行为非常令人惊讶,我很难弄清楚这一点。实际上,在我的特定情况下,不需要为 @cdn 位置设置标题。我很抱歉没有在问题中提到这一点 - 我以为我做到了。考虑到这一点,是否可以进一步简化配置?提前非常感谢!
    • 没问题,map 真的很有用。我在尝试为其他人解决类似问题时遇到了这个问题,所以它非常方便:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-09
    • 2011-07-26
    • 1970-01-01
    • 2022-08-05
    • 2020-02-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多