try_files seemingly looking in wrong directory

I am trying to set up try_files, but am running into an issue: It seems the fallback "empty.png" file is not picked up, even though it is located at /path/to/old_cache_data/empty.png. The files that actually exist (i.e. no fallback) are found without problems. Also interesting enough I am not getting an nginx 404, but the 404 page from the app that is handled by the proxy_pass below.

Stuff I tried so far:

  • Removing the leading slash, which lead to this curious error: open() "/usr//htmlempty.png" failed (2: No such file or directory), Seems weird to me as /usr/html is not specified anywhere as any sort of fallback path.
  • Specifying an absolute path (i.e. try_files $uri /path/to/old_cache_data/empty.png;)

Relevant rules:

# legacy static cache
location /cache/ {
    root /path/to/old_cache_data;
    try_files $uri /empty.png;
}

# pass-through
location / {
    [bunch of proxy-settings]
    proxy_pass [the target url];
}

So, not sure where exactly I'm "holding it wrong". It seems like try_files does not pick up on the root in the location but somehow does a weird fallback.

EDIT: I found out that placing the empty.png in the "cache" sub-folder and changing the try_files to try_files $uri /cache/empty.png; actually works. I do not understand why this is the case though.


Solution 1:

As it stated by the documentation, last parameter of the try_files directive can be

  • a new URI;
  • HTTP error code: =code;
  • named location ID: @location_name.

You are using /empty.png which is treated as a new URI that does not fall under the location /cache { ... } thus being served by the location / { ... }. When you remove the leading slash you get the new URI empty.png that does not fall under any of your locations including location / { ... }. Every server block have some default root path prefix/html where prefix is specified at the compilation time and can be checked with the nginx -V command (looks like yours is /usr/). So as being said by the root directive documentation,

A path to the file is constructed by merely adding a URI to the value of the root directive.

which gives us prefix /usr/ + default root /html + URI empty.png = /usr//htmlempty.png.

When you change last try_files parameter to the /cache/empty.png, this URI is served with the same location /cache { ... } block with the file /path/to/old_cache_data + /cache/empty.png = /path/to/old_cache_data/cache/empty.png. And if you understand all the above information correctly, you should notice that with your configuration you can't access any files in the /path/to/old_cache_data folder except those in the /path/to/old_cache_data/cache subfolder. Check the difference between root and alias directives for better understanding.