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, /etc/apache2/sites-available/

<VirtualHost *:80>

    # mod_wsgi settings
    WSGIDaemonProcess example python-path=/var/www/
    WSGIProcessGroup example
    WSGIScriptAlias / /var/www/

    # Publicly available static files directly available via Apache.
    Alias /static /var/www/
    Alias /pub-uploads /var/www/

    # Restricted access files via Apache mod_xsendfile.
    XSendFile On
    XSendFilePath /var/www/

    # Apache <directory/> directives here...

From the Django application:

$ pip install django-xsendfile

Within the Django application’s

SENDFILE_BACKEND = 'sendfile.backends.xsendfile'

Conventionally this can be overridden in a and imported by 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: , ,

Enabling Apache Digest User Authentication


These notes relate to Debian-based systems running Apache 2.2, so you’ll have to make the appropriate changes to paths, and possibly commands, for your operating system or Linux distro.

The example setup that I’ve provided here allows users with an operational Apache user directory (mod_userdir) to set their own user access permissions, rather than a system-wide approach.

System-wide Settings

The Apache2 configuration files can be found in /etc/apache2/. Update the file /etc/apache2/apache2.conf to include the following directives:

<Directory /home/*/public_html>
    AllowOverride FileInfo AuthConfig Limit
    Options Indexes SymLinksIfOwnerMatch IncludesNoExec

You might simply be able to uncomment existing text within the config file. Among other things, this permits users to enable authentication checking in their public_html directories, or whatever you set the directory name to. You’ll also have to enable Apache’s mod_userdir if it isn’t already enabled:

$ sudo a2enmod userdir

Support for digest authentication is also provided in an Apache module. The digest authentication module is not enabled by default, but can also be enabled using a2enmod:

$ sudo a2enmod auth_digest

If a2enmod isn’t available on your distribution, then you may wish to enable Apache modules by providing a sym link to the appropriate module in the following manner:

ln -s /etc/apache2/mods-available/auth_digest.load /etc/apache2/mods-enable

Password Generation

Passwords are generated using the htdigest tool that ships with the Apache2 distribution. The file created using this tool places username, realm and hashed password together on a colon-delimited line. This file should be placed in a location where Apache cannot serve it up to a client (e.g. don’t place it in /var/www).

In order to add an entry to the password file run the htdigest tool as follows:

$ htdigest -c /directory/path/digest.htpasswd myrealm username

Caution: the -c flag forces htdigest to delete the existing digest  password file, if it already exists. Drop its use of you need to add new entries into the file. You should also replace the values myrealm and username with values appropriate for your system. The realm value is a security context that should be recognisable to the user in order to allow them to provide the correct username and password.

Directory-Level Configuration

You can now create a .htaccess file within each of those directories and subdirectories that you would like to be maintain access control to using digest authentication. Here’s an example .htaccess file that may, for example, be placed immediately within a user’s public_html directory:

AuthType Digest
AuthName "myrealm"
AuthDigestDomain /
AuthUserFile /directory/path/digest.htpasswd
Require valid-user

Here’s an explanation of each of the above Apache directives.

  • The AuthName value is the same value that was given when using the htdigest program (see details above).
  • AuthDigestDomain provides the list of URIs that are in the protection space. These URIs can be absolute or relative and sub-directories of those given are matched also.
  • The value of AuthDigestFile points to the location of the file that was created using the htdigest tool.
  • Require takes two values here, but can take more so that extra requirements are imposed. The values used are used to indicate that the user-level authentication mechanism is being used (rather than group-level) and that only valid users (created using htdigest, as shown above) are granted access.

Now Try It!

Restart apache

$ sudo /etc/init.d/apache2 restart

If all has gone well, you should be challenged for your credentials when you try to browse your protected directories.

Tags: , ,