Serve large file with PHP and nginx X-Accel-Redirect

nginx, php

Solution

Ok - there's a couple of issues here:

1) Puting root inside of a location is a BAD IDEA according to the nginx developers.

2) The internal URL used for telling Nginx that this is an internal redirect shouldn't be exposed to users.

3) I can't see where your download.php file is being served from, so I changed your root location block to use try_files so a request to /download.php would be served by that file rather than index.php.

Your project should be laid out as:

project\
    html - this is the root of your website
    protected  - this directory is not accessible directly

And your Nginx conf should look like:

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;

        root   /path/to/project/html;

        location / {
            try_files $uri /index.php?$args;
        }

        location /protected_files {
            internal;
            alias /path/to/project/protected;
        }
    }
}

It's a good idea not to re-use the same names to mean different things as it's quite confusing. I've changed them so that now `protected` just refers to the actual physical directory that holds the files you want to serve. `protected_files` is just a string that allows Nginx to match the request from the x-accel header.

The only thing that needs changing in your PHP code is to use the correct string to allow Nginx to pickup the internal location:

$aliasedFile = '/download/real-pdf-file.pdf'; //this is the nginx alias of the file path
$realFile = '/path/to/project/protected/real-pdf-file.pdf'; //this is the physical file path
$filename = 'user-pdf-file.pdf'; //this is the file name user will get
header('Cache-Control: public, must-revalidate');
header('Pragma: no-cache');
header('Content-Type: application\pdf');
header('Content-Length: ' .(string)(filesize($realFile)) );
header('Content-Disposition: attachment; filename='.$filename.'');
header('Content-Transfer-Encoding: binary');
header('X-Accel-Redirect: '. $aliasedFile);
exit(0);

Problem

Hi I want user to be able to download PDF file from my server (Windows) configured with nginx PHP. This is my nginx.conf (server block) ``` http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root html; index index.php; } location /download { internal; alias /protected; } } } ``` ..and the PHP file (header part) ``` $file = '/download/real-pdf-file.pdf'; //this is the physical file path $filename = 'user-pdf-file.pdf'; //this is the file name user will get header('Cache-Control: public, must-revalidate'); header('Pragma: no-cache'); header('Content-Type: application\pdf'); header('Content-Length: ' .(string)(filesize($file)) ); header('Content-Disposition: attachment; filename='.$filename.''); header('Content-Transfer-Encoding: binary'); header('X-Accel-Redirect: '. $file); ``` The file is call from URL like this: ``` download.php?id=a2a9ca5bd4a84294b421fdd9ef5e438ded7fcb09 ``` I've try few example/solution from here but so far none is working. The file is large (between 250 to 400 MB each) and each user will be able to download 4 files. There is no issue with downloading part using PHP, only the nginx configuration that seem not working. No error log detected.

Original source