我的 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,我该如何修复它?预先感谢!
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;
}
使用Nginx多年后,我确实遇到过这个问题,并且发现很少有讨论,我认为大多数开发人员都没有意识到
if
条件与try_files
冲突...
无论如何,@M Somerville 出色的解释和解决方案的另一个选择可能是简单地反转你的
if
语句的逻辑,如下所示:
https://serverfault.com/a/579739/144798
在该线程/答案中,OP正在使用
if ($args ~ "api_url") {
,建议是像 if ($arg_api_url != '') {
一样反转,以避免这些请求陷入if
的黑洞......