nginx – try files on multiple named location or server

Let’s start from the simplest case: How to serve files locally first, and proxy the request to other backend/internal server if not found?

listen 192.168.1.10:80;
...
location /static/ {
    try_files $uri @static_svr1;
}

location @static_svr1{
    proxy_pass http://192.168.1.11$uri;
}

That’s simple! What if we want to try files on multiple backend server?
Say, if files do not locally exist, try static_svr1, if not found there, try static_svr2, if still not found, try static_svr3, and return 404 if finally no luck.

We know that there can be only one named location for the try_files directive. so it can’t help in this case.

Fortunately, from the nginx documentation:

...
In this example, the directive try_files

try_files $uri $uri/ /index.php?q=$uri&$args;
Is basically the same as this:

location / {
  error_page     404 = @drupal;
  log_not_found  off;
}

location @drupal {
  rewrite ^ /index.php?q=$uri last; # for drupal 6
}

From the example, it may be possible to use error_page directive chain to implement our setup:

location /static/ {
	try_files $uri @static_svr1;
}
location @static_svr1{
	proxy_pass http://192.168.1.11$uri;
	error_page 404 = @static_svr2;
}

location @static_svr2{
	proxy_pass http://192.168.1.12$uri;
	error_page 404 = @static_svr3;
}

location @static_svr3{
	proxy_pass http://192.168.1.13$uri;
}

However, this doesn’t work properly. let’s see what would happen with the above config.
Assuming that file /static/0.html is on static_svr0, …, /static/3.html is on static_svr3. Here’s the result:

URL Accessed status code
http://192.168.1.10/static/0.html 200
http://192.168.1.10/static/1.html 200
http://192.168.1.10/static/2.html 404
http://192.168.1.10/static/3.html 404

Only static_svr1 works as expected. Also, the access_log of static_svr2 shows that 192.168.1.10 never tried to access it. Why? Because there’s no 404 error_page processing for proxy_pass unless proxy_intercept_errors is on. let’s add it and test again:

location /static/ {
	try_files $uri @static_svr1;
}
location @static_svr1{
	proxy_pass http://192.168.1.11$uri;
	proxy_intercept_errors on;
	error_page 404 = @static_svr2;
}

location @static_svr2{
	proxy_pass http://192.168.1.12$uri;
	proxy_intercept_errors on;
	error_page 404 = @static_svr3;
}

location @static_svr3{
	proxy_pass http://192.168.1.13$uri;
}
URL Accessed status code
http://192.168.1.10/static/0.html 200
http://192.168.1.10/static/1.html 200
http://192.168.1.10/static/2.html 200
http://192.168.1.10/static/3.html 404

static_svr2 now works! But why not static_svr3?  The log  also suggests that it’s never accessed? Maybe the error_page directive doesn’t work for static_svr3, search nginx documentation with keyword error_page, I find the very recursive_error_pages directive, it mentioned the word “chain”, that’s just what we want.  Adjust the config again:

location /static/ {
	try_files $uri @static_svr1;
}
location @static_svr1{
	proxy_pass http://192.168.1.11$uri;
	proxy_intercept_errors on;
	recursive_error_pages on;
	error_page 404 = @static_svr2;
}

location @static_svr2{
	proxy_pass http://192.168.1.12$uri;
	proxy_intercept_errors on;
	recursive_error_pages on;
	error_page 404 = @static_svr3;
}

location @static_svr3{
	proxy_pass http://192.168.1.13$uri;
}

Access the test URLs again, now all return status code 200, perfect!

This entry was posted in System Administration, Web and tagged , , , , . Bookmark the permalink.

2 Responses to nginx – try files on multiple named location or server

  1. Fidel says:

    Works like a charm!!

    Thanks :)

  2. Misiek says:

    You can propably try with putting those two additional directives on server block, so you will avoid duplicating those in locations.

Leave a Reply