-->

Nginx serving static files with 404 status code?

2019-09-16 18:03发布

问题:

I'm trying to let Nginx serve my authenticated admin users some static files with PHP. This works great, I get back the file. But it's served with a 404 status code..

I'm using the following (Symfony / Silex) php code:

use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

$filePath = '/usr/share/nginx/project/src/path/to/my/protected/static/dir/' . $file;
if (empty($file) || !is_readable($filePath)) {
    $app->abort(404);
}

$response = new BinaryFileResponse($filePath);
$response->trustXSendfileTypeHeader();
$response->setPrivate();
$response->setContentDisposition(
    ResponseHeaderBag::DISPOSITION_INLINE,
    $file,
    iconv('UTF-8', 'ASCII//TRANSLIT', $file)
);
$response->headers->addCacheControlDirective('must-revalidate', true);
return $response;

And here's my nginx config:

server {
    listen          443;
    listen          [::]:443;

    root            /usr/share/nginx/project/web;
    index           index.php;
    error_page      401 403 404 /404.html;

    server_name     example.com;

    rewrite ^/(.*)/$ /$1 permanent;

    location / {
        # First attempt to serve request as file, then
        # as directory, then let php handle the file
        try_files $uri $uri/ /index.php$is_args$args;
        index index.php;
        autoindex off;

        location ~*  \.(svg|jpg|jpeg|png|gif|ico|css|js)$ {
            expires 150d;
        }
    }

    location ~ \.php$ {
        set $path_info $fastcgi_path_info;
        fastcgi_index index.php;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;

        try_files $uri $uri/ /index.php$is_args$args;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;

        include fastcgi_params;
        fastcgi_param APP_ENV production;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

The protected dir lies outside of the root in the nginx config (/usr/share/nginx/project/web).

I've found these kind of error messages in the logs:

open() "/usr/share/nginx/project/web/admin/static/admin.js" failed 
(2: No such file or directory), 
request: "GET /admin/static/admin.js HTTP/1.1"

Where /admin/static/admin.js was indeed the requested url.

Update 1

It looks like nginx always tries to open the url and adds an entry to the error log even php handles the response just fine.

Even if I replace all the php code for just: return new Response('test', 200); the response code for the body 'test' is still 404...

I also tried adding an extra location block in my nginx config:

location /protected_admin_files {
    internal;
    alias /usr/share/nginx/project/src/path/to/my/protected/static/dir;
}

And then try to redirect to the file like this:

return new Response('', 200, [
    'X-Accel-Redirect' => '/protected_admin_files/' . $file
]);

But also without luck. Same 404 with the right response body...

回答1:

I've found the cause myself..

This was what I found in the error log once I set the error level to debug (here's the full log)

...
[debug] rewrite phase: 1
[debug] http script regex: "^/(.*)/$"
[notice] "^/(.*)/$" does not match "/admin/static/admin.js", request: "GET /admin/static/admin.js HTTP/1.1", host: "example.com"
[debug] test location: "/"
[debug] test location: "protected_admin_files"
[debug] test location: ~ "\.(svg|jpg|jpeg|png|gif|ico|css|js)$"
[debug] using configuration "\.(svg|jpg|jpeg|png|gif|ico|css|js)$"
...
[debug] http filename: "/usr/share/nginx/project/web/admin/static/admin.js"
[debug] add cleanup: 00000000025AE108
[error] open() "/usr/share/nginx/project/web/admin/static/admin.js" failed (2: No such file or directory), client: 24.132.134.203, server: example.com, request: "GET /admin/static/admin.js HTTP/1.1", host: "example.com"
[debug] http finalize request: 404, "/admin/static/admin.js?" a:1, c:1
[debug] http special response: 404, "/admin/static/admin.js?"
[debug] internal redirect: "/index.php?"
...

Apparently, the nested location in my first first location / block was causing the 404.

location / {
    try_files $uri $uri/ /index.php$is_args$args;
    index index.php;
    autoindex off;

    location ~*  \.(svg|jpg|jpeg|png|gif|ico|css|js)$ {
        expires 150d;
    }
}

It got matched because of the extension, making Nginx go looking for the file. Since it's then not found, the 404 is set and apparently not overwritten later in the process when php returns the X-Accel-Redirect header :(.