How to combine try_files and sendfile on Nginx?

hcalves asked:

I need Nginx to serve a file relative from document root if it exists, then fallback to an upstream server if it doesn’t. This can be accomplished with something like:

server {
    listen       80;
    server_name  localhost;

    location / {
        root /var/www/nginx/;
        try_files $uri @my_upstream;
    }

    location @my_upstream {
        internal;
        proxy_pass  http://127.0.0.1:8000;
    }
}

Fair enough. The problem is, my upstream is not serving the contents of URI directly, but instead, returning X-Accel-Redirect with a location relative to document root (it generates this file on-the-fly):

% curl -I http://127.0.0.1:8000/animals/kitten.jpg__100x100__crop.jpg
HTTP/1.0 200 OK
Date: Mon, 26 Nov 2012 20:58:25 GMT
Server: WSGIServer/0.1 Python/2.7.2
X-Accel-Redirect: animals/kitten.jpg__100x100__crop.jpg
Content-Type: text/html; charset=utf-8

Apparently, this should work. The problem though is that Nginx tries to serve this file from some internal default document root instead of using the one specified in the location block:

2012/11/26 18:44:55 [error] 824#0: *54 open() "/usr/local/Cellar/nginx/1.2.4/htmlanimals/kitten.jpg__100x100__crop.jpg" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /animals/kitten.jpg__100x100__crop.jpg HTTP/1.1", upstream: "http://127.0.0.1:8000/animals/kitten.jpg__100x100__crop.jpg", host: "127.0.0.1:80"

How do I force Nginx to serve the file relative to the right document root? According to XSendfile documentation the returned path should be relative, so my upstream is doing the right thing.

My answer:


You need to have your root directive under server, and not under location. This is one of the most common nginx configuration mistakes.


View the full question and answer on Server Fault.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.