I have a simple location
block in my nginx config which matches static files for my website. What I want to do, is to check if the file exists using try_files
, and if it doesn't, redirect to a URL (in this case specified in the @cdn
location block). I also want to set some CORS headers.
Below is the relevant configuration.
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;
}
The problem is that I get a 404 response if the file does not exist, instead of a 301 redirect. The configuration worked/works fine before adding the CORS headers. If I remove the handling of the headers, everything works as intended, and I get a 301 response back.
Now I have done a bit of reading about why the if directive is bad and should be avoided, but I still don't know why it breaks my configuration. If I understood correctly, it has something to do with either if
or add_header
being part of a rewrite module or something like that, and I guess that conflicts with try_files
. Perhaps I am not accurate here, but either way I am not sure how to fix it.
Why does the presence of if
and/or add_header
make nginx give me a 404 instead of a 301 when a file could not be found, and how do I fix it? Thanks in advance!
http://agentzh.blogspot.co.uk/2011/03/how-nginx-location-if-works.html might be of interest to you in understanding how if
works. In your case, when the if
condition matches, the request is now being served within the if
context, and try_files
is not inherited by that context. Or as https://www.digitalocean.com/community/tutorials/understanding-the-nginx-configuration-file-structure-and-configuration-contexts says "Another thing to keep in mind when using an if context is that it renders a try_files directive in the same context useless."
Also, if the try_files
falls back to @cdn
then any headers you've added previously are forgotten, it starts again in the new location
block, and so the headers need to be added there.
As to how to fix it; you can set variables inside if
, and add_header
ignores an empty value, so something like this should work:
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;
}
Edit: You don't care about the headers in the @cdn fallback, in which case you should be able to have something like this:
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With