Search code examples
nginxsingle-page-applicationreverse-proxy

try_files redirecting instead of using fallback directory?


I'm running an nginx SPA application.However for requests I would like a fallback/fallthrough approach:

I have the main directory / which has the main /index.html as well as "default" icons. However it also contains a subdirectory /local/ which contains (potentially) values from the default directory - overwriting those existing ones. (This is to allow a docker volume, to provide specific favicon.ico etc from the host OS and modify that without rebuilding the docker).

Initially I had the following proxy directive:

root /var/www/html;
location / {
    # redirect everything to show the main SPA application
    try_files $uri $uri/ /index.html;
    expires 7d;
}
location = /index.html { }

Which works, however it does not prefer the files under /local unless specified in the request.

I then tried to add a longer try_files list:

root /var/www/html;
location / {
    # redirect everything to show the main SPA application
    try_files $uri/local $uri/local/ $uri $uri/ /index.html;
    expires 7d;
}
location = /index.html { }

However now if I go to the mainpage localhost:80 a redirect (301) to localhost//local/ happens

I feel I do not understand the try_files function. And secondly I wonder how above can be done. In summary this is the lookup I wish nginx to do (notice no redirection for frontend):

localhost/ => ./index.html
localhost/favicon.ico => ./local/favicon.ico //IF that file exists
localhost/favicon.ico => ./favicon.ico //IF ./local/favicon does not exist
localhost/unexistingfile => ./index.html

How would I do this? Or is this impossible at nginx level and should I do this in the application layer?


Solution

  • Considering the request //localhost/favicon.ico, the value of $uri is set to /favicon.ico.

    The file term $uri/local will cause Nginx to search for a file at /var/www/html/favicon.ico/local, which is not your intent.

    The correct file term is /local$uri, which will cause Nginx to search for a file at /var/www/html/local/favicon.ico.


    From the try_files documentation:

    It is possible to check directory’s existence by specifying a slash at the end of a name

    When Nginx finds a directory using try_files in this way, it issues an external redirection to a URL with a trailing /. URLs with a trailing / that resolve to a directory are then processed by the index module.

    Your file term $uri/local/ is matching the directory /var/www/html/local for the //localhost request, which causes the unwanted redirection to //localhost/local/.


    In summary, use:

    try_files /local$uri $uri $uri/ /index.html;
    

    First, the file term /local$uri will look for files under the local subdirectory.

    Second, the file term $uri will look for files starting from the root.

    Third, the file term $uri/ will perform normal index processing where the URL resolves to a directory.

    The final parameter is a URI, which causes Nginx to generate an internal redirection to index.html if none of the file terms found a match.