Django, X-SendFile and Apache
Posted by Paul on 5th February 2014
Static File Access Management
Django documentation advises against using an application to directly serve static files when in a production environment. A dedicated HTTP server, optimised for serving static files, should be used for this purpose. That’s fine for serving publicly accessible files. But serving static files which require user access permission checking necessarily involves routing the download request via application code.
It’s possible to get the best of both Django view processing, where access permissions can be managed and dedicated HTTP server download handling, by using a HTTP server extension that processes response headers applied by the web application. The mod_xsendfile Apache module is one such module. Used with django-sendfile, application management of file downloads can be cleanly separated into interface and implementation.
django-xsendfile and Apache mod_xsendfile Installation
Install the mod_xsendfile module:
$ sudo apt-get install libapache2-mod-xsendfile
The mod_xsendfile module is usually enabled by the install scripts. Use a2enmod
to further manage its enabled status.
Enable X-SENDFILE header processing within the application’s Apache virtual host. For instance, a virtual host for for a Django application to serve example.com, /etc/apache2/sites-available/example.com
:
<VirtualHost *:80> ServerName example.com # mod_wsgi settings WSGIDaemonProcess example python-path=/var/www/example.com/app WSGIProcessGroup example WSGIScriptAlias / /var/www/example.com/app/example/wsgi.py # Publicly available static files directly available via Apache. Alias /static /var/www/example.com/app/static Alias /pub-uploads /var/www/example.com/pub-uploads # Restricted access files via Apache mod_xsendfile. XSendFile On XSendFilePath /var/www/example.com/priv-uploads # Apache <directory/> directives here... </VirtualHost>
From the Django application:
$ pip install django-xsendfile
Within the Django application’s settings.py
:
SENDFILE_BACKEND = 'sendfile.backends.xsendfile'
Conventionally this can be overridden in a local_settings.py
and imported by settings.py
for a development environments:
SENDFILE_BACKEND = 'sendfile.backends.development'
The Django view used to manage download access permissions may then look something like the following:
from django.views.generic.base import View from sendfile import sendfile class DownloadFile(View): def get(self, request): # Get user access rights and the file's file-system path. # ... # If access denied return HttpResponseForbidden(), else: return sendfile(request, file_path)
The sendfile API also provides response header management, such as Content-Disposition
header content:
def sendfile(request, filename, attachment=False, attachment_filename=None, mimetype=None, encoding=None)