Secure downloads using X-Accel-Redirect

It is possible to use X-Accel-Redirect (also known as X-SendFile) to restrict access in your webspace to certain files and folders in your PHP or Python application.

We suggest this method especially for very complex file permission management (ACLs, ...), but even for some very simple applications it might be a better solution than classic HTTP authentication as you can work with 'sessions' to manage file permissions in a very flexible and dynamic way.

Example

Asume that you have a website running under http://www.mydomain.com/ and you want a certain file, e.g. "secret.pdf" to be delivered to the end-user's browser only after successful authentication via password or cookie.

This is how your example configuration for the domain www.mydomain.com might look like:

HTTP
Document root: /srv/http/web1234/www.mydomain.com/htdocs
WSGI/PHP
URI "/" points to "WSGI Applikation"

Now, create a secure directory outside your document root, e.g.: /srv/http/web1234/www.mydomain.com/protected

Put the file "secret.pdf" into that directory. The absolute path to this file is now: /srv/http/web1234/www.mydomain.com/protected/secret.pdf

Now you have to adjust your domain configuration and add the secure directory:

Static
URI "/protected/" points to the path "/srv/http/web1234/www.mydomain.com/protected/".
Make sure to check the checkbox that reads "Internal requests only".

This new static URI is not accessible from the "outside", nevertheless: your application can use the X-Accel-Redirect header to create a valid reference to your file.

Now assume that a visitor of your website opens the page http://www.mydomain.com/. Your application displays some kind of login screen and after successful validation of the user's credentials your user now requests the page http://www.mydomain.com/internal/. On this page your application creates a link to the protected file "secret.pdf".

You can choose the name and path of the URL freely, but you have to make sure that the references file does not exist in your directory "/protected/". In our example we assume the URL links to http://www.mydomain.com/download/secret.pdf". The user clicks this link and your application receives a regular GET request for "/download/secret.pdf".

Your application might now be able to verify the user's credentials / session id / whatever to decide wether access to download the file should be granted or not.

Now assume you want to grant permission to the user to download the file. Instead of reading the file into your application and delivering it directly (which is very inefficient), you can do the following:

Deliver an empty HTTP response (HTTP status code 200) with the following header information:

Example for PHP

header("Content-Type: application/pdf");
header("X-Accel-Redirect: /protected/geheim.pdf");

Example for Python (Flask)

response = Flask.make_response()
response.headers['Content-Type'] = 'application/pdf'
response.headers['X-Accel-Redirect'] = '/protected/secret.pdf'

Attention!

X-Accel-Redirect only works with absolute URI that start with a leading "/".

The web servers catch this HTTP response from the app servers and deliver the file /srv/http/web1234/www.mydomain.com/protected/secret.pdf using the correct content-type.

This method usually saves a bunch of memory and is far superior to all the different "sendfile" alternatives!