Django, X-SendFile and Apache

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)
Tags: , ,