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

Hi, I want the user to be able to download a PDF file from my server (Windows) configured using 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 a PHP file (part of the header)

 $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); 

A file is a call from a URL, for example:

 download.php?id=a2a9ca5bd4a84294b421fdd9ef5e438ded7fcb09 

I tried a few examples / solutions from here, but so far no one is working. The file is large (from 250 to 400 MB each), and each user will be able to download 4 files.

There is no problem loading the part using PHP, only the nginx configuration, which seems to be broken. No error log found.

+4
source share
2 answers

Good - there are a few problems here:

1) The root root inside the location of the BAD IDEA according to the nginx developers.

2) The internal URL used to tell Nginx that it is an internal redirect should not be exposed to users.

3) I canโ€™t see where your download.php file is located, so I changed your root location block to use try_files, so the request for /download.php will be served by this file, not 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 this:

 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 is a good idea not to repeat the use of the same names in order to keep in mind different things, as this is quite confusing. I changed them, so now protected just refers to the actual physical directory in which the files you want to serve are stored. protected_files is just a string that allows Nginx to match the request from the x-accel header.

The only thing you need to change in your PHP code is to use the correct line so that Nginx can pick up 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); 
+10
source

Based on Danack's recommendation and solution and some clarifications from Carsten, I found that in Windows Serve we need to set the full path in an alias as follows:

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

Note that an additional slash is required (in my case, Windows 7 Pro for development, Windows Server 2008 for deployment). The only problem is that now I need to test concurrent downloads to make sure that the server resources are running.

I am new to nginx as it seems to me that I am switching from Apache faster. Thanks to the guy for enlightenment! I'm glad to be part of the Stackoverflow community :)

+3
source

All Articles