Reputation: 742
I need to serve multiple versions of multiple Single Page Applications (SPAs) from the one server.
If a URL specifies a version that does not exist on the server, I want to serve a fallback version instead.
Some of the versions are actually symlinks to other versions.
# /var/www/apps/example-app
0.1.0
0.1.1
0.1.2
fooversion -> /var/www/apps/example-spa/0.1.2
latest -> /var/www/apps/example-spa/0.1.1
The version latest
is the one I want to use as a fallback.
All SPAs have an index.html
file.
I specifically do not want to serve a mix of files from different versions. If a version exists on the server, all requests for that version should be served from that folder.
# excerpt from nginx config
...
location ~ ^/apps/(?<appname>.+?)/(?<appversion>.+?)/(?<suffix>.*)
{
# 'latest' is the name of the folder with the fallback version
set $effectiveversion latest;
# NOTE: file check does not take into account `root` directive, use full path
if (-f /var/www/apps/$appname/$appversion/index.html) {
set $effectiveversion $appversion;
}
# try path, then force to SPA index
try_files /apps/$appname/$effectiveversion/$suffix /apps/$appname/$effectiveversion/index.html;
}
# catch requests that end without a trailing slash
location ~ ^/apps/(?<appname>.+?)/(?<appversion>[^\/]+)$
{
try_files /NONEXISTENTFILE /apps/$appname/$appversion/;
}
...
I am aware that the if
could be a problem, given the reputation it has in nginx configuration.
(Note: by suffix I mean anything after a slash that is after the version, so both /file.extension
and /some/folder/that/does/notexist/
are suffixes. Files should be served if they exist, and all subfolders should serve the version's index.html
.
This code currently works for the following URLs:
https://example.com/apps/bar-app/nonexistentversion/nonexistentsuffix
https://example.com/apps/bar-app/nonexistentversion/nonexistentsuffix/
https://example.com/apps/bar-app/nonexistentversion
https://example.com/apps/bar-app/nonexistentversion/
https://example.com/apps/bar-app/nonexistentversion/existentsuffix
https://example.com/apps/bar-app/existentversion/existentsuffix
https://example.com/apps/bar-app/existentversion/
https://example.com/apps/bar-app/existentversion
But it does not work for these:
https://example.com/apps/bar-app/existentversion/nonexistentsuffix
https://example.com/apps/bar-app/existentversion/nonexistentsuffix/
Currently these last two return 404s.
==> /var/log/nginx/error.log <==
2020/06/03 14:23:15 [error] 7100#7100: *1289803 open() "/var/www/apps/example-app/fooversion/somefrontendroute" failed (2: No such file or directory), client: ..., server: , request: "GET /apps/example-app/fooversion/somefrontendroute HTTP/1.1", host: "static.bar.com", referrer: ...
==> /var/log/nginx/access.log <==
[03/Jun/2020:14:23:15 +1000] ... - "GET /apps/example-app/fooversion/somefrontendroute HTTP/1.1" 404 209 - 0.000 - - - 1591158195.572 -"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36" "..., ..."
Is there a way to cover these last few cases? Or maybe even a cleaner way to handle this whole problem?
(99% Richard Smith's answer, I just added a rewrite)
# cover case where version has no trailing `/`
rewrite ^/apps/([^/]+?)/([^/]+)$ /apps/$1/$2/;
location ~ ^/apps/(?<app>.+?)/(?<version>.+?)/(?<rest>.*)$
{
try_files
/apps/$app/$version/$rest
/apps/$app/$version/index.html
/apps/$app/latest/$rest
/apps/$app/latest/index.html
;
}
If a URI does not exist, it will serve up the index instead of a 404. This was acceptable for me
Upvotes: 1
Views: 926
Reputation: 49692
You can probably remove the if
block as the try_files
directive is effectively the same as if (-f
.
The last parameter of a try_files
statement is not a file term. By adding =404
to the end of the statement, your last parameter becomes a regular file term, which may be the underlying cause of your problem cases.
See this document for details.
For example:
location ~ ^/apps/(?<appname>.+?)/(?<appversion>.+?)/(?<suffix>.*)$
{
try_files
$uri
/apps/$appname/$appversion/index.html
/apps/$appname/latest/$suffix
/apps/$appname/latest/index.html
=404
;
}
Upvotes: 1