Nginx

If you serve Django behind Nginx, then you can delegate the file download service to Nginx and get increased performance:

  • lower resources used by Python/Django workers ;
  • faster download.

See Nginx X-accel documentation [1] for details.

Configure some download view

Let’s start in the situation described in the demo application:

We are to make it more efficient with Nginx.

Note

Examples below are taken from the demo project.

Write tests

Use django_downloadview.nginx.assert_x_accel_redirect() function as a shortcut in your tests.

demo/demoproject/nginx/tests.py:

"""Test suite for demoproject.nginx."""
from django.core.files import File
from django.core.urlresolvers import reverse_lazy as reverse

from django_downloadview.nginx import assert_x_accel_redirect
from django_downloadview.test import temporary_media_root

from demoproject.download.models import Document
from demoproject.download.tests import DownloadTestCase


class XAccelRedirectDecoratorTestCase(DownloadTestCase):
    @temporary_media_root()
    def test_response(self):
        """'download_document_nginx' view returns a valid X-Accel response."""
        document = Document.objects.create(
            slug='hello-world',
            file=File(open(self.files['hello-world.txt'])),
        )
        download_url = reverse('download_document_nginx',
                               kwargs={'slug': 'hello-world'})
        response = self.client.get(download_url)
        self.assertEquals(response.status_code, 200)
        # Validation shortcut: assert_x_accel_redirect.
        assert_x_accel_redirect(
            self,
            response,
            content_type="text/plain; charset=utf-8",
            charset="utf-8",
            basename="hello-world.txt",
            redirect_url="/download-optimized/document/hello-world.txt",
            expires=None,
            with_buffering=None,
            limit_rate=None)
        # Check some more items, because this test is part of
        # django-downloadview tests.
        self.assertFalse('ContentEncoding' in response)
        self.assertEquals(response['Content-Disposition'],
                          'attachment; filename=hello-world.txt')

Right now, this test should fail, since you haven’t implemented the view yet.

Setup Django

At the end of this setup, the test should pass, but you still have to setup Nginx!

You have two options: global setup with a middleware, or per-view setup with decorators.

Global delegation, with XAccelRedirectMiddleware

If you want to delegate all file downloads to Nginx, then use django_downloadview.nginx.XAccelRedirectMiddleware.

Register it in your settings:

MIDDLEWARE_CLASSES = (
    # ...
    'django_downloadview.nginx.XAccelRedirectMiddleware',
    # ...
)

Setup the middleware:

NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT = MEDIA_ROOT  # Could be elsewhere.
NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_URL = '/proxied-download'

Optionally fine-tune the middleware. Default values are None, which means “use Nginx’s defaults”.

NGINX_DOWNLOAD_MIDDLEWARE_EXPIRES = False  # Force no expiration.
NGINX_DOWNLOAD_MIDDLEWARE_WITH_BUFFERING = False  # Force buffering off.
NGINX_DOWNLOAD_MIDDLEWARE_LIMIT_RATE = False  # Force limit rate off.

Local delegation, with x_accel_redirect decorator

If you want to delegate file downloads to Nginx on a per-view basis, then use django_downloadview.nginx.x_accel_redirect() decorator.

demo/demoproject/nginx/views.py:

"""Views."""
from django_downloadview.nginx import x_accel_redirect

from demoproject.download import views


download_document_nginx = x_accel_redirect(
    views.download_document,
    source_dir='/var/www/files',
    destination_url='/download-optimized')

And use it in som URL conf, as an example in demo/demoproject/nginx/urls.py:

"""URL mapping."""
from django.conf.urls import patterns, url


urlpatterns = patterns('demoproject.nginx.views',
    url(r'^document-nginx/(?P<slug>[a-zA-Z0-9_-]+)/$',
        'download_document_nginx', name='download_document_nginx'),
)

Note

In real life, you’d certainly want to replace the “download_document” view instead of registering a new view.

Setup Nginx

See Nginx X-accel documentation [1] for details.

Here is what you could have in /etc/nginx/sites-available/default:

charset utf-8;

# Django-powered service.
upstream frontend {
    server 127.0.0.1:8000 fail_timeout=0;
}

server {
    listen 80 default;

    # File-download proxy.
    #
    # Will serve /var/www/files/myfile.tar.gz when passed URI
    # like /optimized-download/myfile.tar.gz
    #
    # See http://wiki.nginx.org/X-accel
    # and https://github.com/benoitbryon/django-downloadview
    location /proxied-download {
        internal;
        # Location to files on disk.
        # See Django's settings.NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT
        alias /var/www/files/;
    }

    # Proxy to Django-powered frontend.
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://frontend;
    }
}

... where specific configuration is the location /optimized-download section.

Note

/proxied-download is not available for the client, i.e. users won’t be able to download files via /optimized-download/<filename>.

Warning

Make sure Nginx can read the files to download! Check permissions.

Common issues

Unknown charset "utf-8" to override

Add charset utf-8; in your nginx configuration file.

open() "path/to/something" failed (2: No such file or directory)

Check your settings.NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT in Django configuration VS alias in nginx configuration: in a standard configuration, they should be equal.

References

[1](1, 2) http://wiki.nginx.org/X-accel

Table Of Contents

Previous topic

Optimizations

Next topic

Testing download views

This Page