Deploying Django

This chapter covers the last essential step of building a Django application: deploying it to a production server.

If you’ve been following along with our ongoing examples, you’ve likely been using the runserver, which makes things very easy – with runserver, you don’t have to worry about Web server setup. But runserver is intended only for development on your local machine, not for exposure on the public Web.

To deploy your Django application, you’ll need to hook it into an industrial-strength Web server such as Apache. In this chapter, we’ll show you how to do that – but, first, we’ll give you a checklist of things to do in your codebase before you go live.

Preparing Your Codebase for Production

Deployment checklist

The Internet is a hostile environment. Before deploying your Django project, you should take some time to review your settings, with security, performance, and operations in mind.

Django includes many security features. Some are built-in and always enabled. Others are optional because they aren’t always appropriate, or because they’re inconvenient for development. For example, forcing HTTPS may not be suitable for all websites, and it’s impractical for local development.

Performance optimizations are another category of trade-offs with convenience. For instance, caching is useful in production, less so for local development. Error reporting needs are also widely different. The following checklist includes settings that:

  • must be set properly for Django to provide the expected level of security,
  • are expected to be different in each environment,
  • enable optional security features,
  • enable performance optimizations; and
  • provide error reporting.

Many of these settings are sensitive and should be treated as confidential. If you’re releasing the source code for your project, a common practice is to publish suitable settings for development, and to use a private settings module for production. Some of the checks described below can be automated using the
--deploy option of the check command. Be sure to run it against your production settings file as described in the option’s documentation.

Critical settings


The secret key must be a large random value and it must be kept secret.

Make sure that the key used in production isn’t used anywhere else and avoid committing it to source control. This reduces the number of vectors from which an attacker may acquire the key. Instead of hard-coding the secret key in your settings module, consider loading it from an environment variable:

import os
SECRET_KEY = os.environ['SECRET_KEY']

or from a file:

with open('/etc/secret_key.txt') as f:


You must never enable debug in production.

When we created a project in Chapter 1, the command django-admin startproject created a file with DEBUG set to True. Many internal parts of Django check this setting and change their behavior if DEBUG mode is on.

For example, if DEBUG is set to True, then:

  • All database queries will be saved in memory as the object django.db.connection.queries. As you can imagine, this eats up memory!
  • Any 404 error will be rendered by Django’s special 404 error page (covered in Chapter 3) rather than returning a proper 404 response. This page contains potentially sensitive information and should not
    be exposed to the public Internet.
  • Any uncaught exception in your Django application – from basic Python syntax errors to database errors to template syntax errors – will be rendered by the Django pretty error page that you’ve likely come to know and love. This page contains even more sensitive information than the 404 page and should never be exposed to the public.

In short, setting DEBUG to True tells Django to assume only trusted developers are using your site. The Internet is full of untrustworthy hooligans, and the first thing you should do when you’re preparing your application for deployment is set DEBUG to False.

Environment-specific Settings


When DEBUG = False, Django doesn’t work at all without a suitable value for ALLOWED_HOSTS. This setting is required to protect your site against some CSRF attacks. If you use a wildcard, you must perform your own validation of the Host HTTP header, or otherwise ensure that you aren’t vulnerable to this category of attack.


If you’re using a cache, connection parameters may be different in development and in production. Cache servers often have weak authentication. Make sure they only accept connections from your application servers. If you’re using Memcached, consider using cached sessions to improve performance.


Database connection parameters are probably different in development and in production. Database passwords are very sensitive. You should protect them exactly like SECRET_KEY. For maximum security, make sure database servers only accept connections from your application servers. If you haven’t set up backups for your database, do it right now!

If your site sends emails, these values need to be set correctly.


Static files are automatically served by the development server. In production, you must define a STATIC_ROOT directory where collectstatic will copy them.


Media files are uploaded by your users. They’re untrusted! Make sure your web server never attempts to interpret them. For instance, if a user uploads a .php file, the web server shouldn’t execute it. Now is a good time to check your backup strategy for these files.


Any website which allows users to log in should enforce site-wide HTTPS to avoid transmitting access tokens in clear. In Django, access tokens include the login/password, the session cookie, and password reset tokens. (You can’t do much to protect password reset tokens if you’re sending them by email.)

Protecting sensitive areas such as the user account or the admin isn’t sufficient, because the same session cookie is used for HTTP and HTTPS. Your web server must redirect all HTTP traffic to HTTPS,
and only transmit HTTPS requests to Django. Once you’ve set up HTTPS, enable the following settings.


Set this to True to avoid transmitting the CSRF cookie over HTTP accidentally.


Set this to True to avoid transmitting the session cookie over HTTP accidentally.

Performance optimizations

Setting DEBUG = False disables several features that are only useful in development. In addition, you can tune the following settings.


Enabling persistent database connections can result in a nice speed-up when connecting to the database accounts for a significant part of the request processing time. This helps a lot on virtualized hosts with limited network performance.


Enabling the cached template loader often improves performance drastically, as it avoids compiling each template every time it needs to be rendered. See the template loaders docs for more information.

Error Reporting

By the time you push your code to production, it’s hopefully robust, but you can’t rule out unexpected errors. Thankfully, Django can capture errors and notify you accordingly.


Review your logging configuration before putting your website in production, and check that it works as expected as soon as you have received some traffic.


ADMINS will be notified of 500 errors by email. MANAGERS will be notified of 404 errors. IGNORABLE_404_URLS can help filter out spurious reports.

Error reporting by email doesn’t scale very well. Consider using an error monitoring system such as Sentry before your inbox is flooded by reports. Sentry can also aggregate logs.

Customize The Default Error Views

Django includes default views and templates for several HTTP error codes. You may want to override the default templates by creating the following templates in your root template directory: 404.html, 500.html, 403.html, and 400.html. The default views should suffice for 99% of Web applications, but if you desire to customize them, see these instructions which also contain details about the default templates:

  • http_not_found_view
  • http_internal_server_error_view
  • http_forbidden_view
  • http_bad_request_view

Using a Virtualenv

If you install your project’s Python dependencies inside a virtualenv, you’ll need to add the path to this virtualenv’s site-packages directory to your Python path as well. To do this, add an additional path to your WSGIPythonPath directive, with multiple paths separated by a colon (:) if using a UNIX-like system, or a semicolon (;) if using Windows. If any part of a directory path contains a space character, the complete argument string to WSGIPythonPath must be quoted:

WSGIPythonPath /path/to/ 

Make sure you give the correct path to your virtualenv, and replace python3.X with the correct Python version (e.g. python3.4).

Using Different Settings for Production

So far in this book, we’ve dealt with only a single settings file: the generated by django-admin startproject. But as you get ready to deploy, you’ll likely find yourself needing multiple settings files to keep your development environment isolated from your production environment. (For example, you probably won’t want to change DEBUG from False to True whenever you want to test code changes on your local machine.) Django makes this very easy by allowing you to use multiple settings files. If you’d like to organize your settings files into production and development settings,
you can accomplish this in one of three ways:

  • Set up two full-blown, independent settings files.
  • Set up a base settings file (say, for development) and a second (say, production) settings file that merely imports from the first one and defines whatever overrides it needs to define.
  • Use only a single settings file that has Python logic to change the settings based on context.

We’ll take these one at a time. First, the most basic approach is to define two separate settings files. If you’re following along, you’ve already got Now, just make a copy of it called (We made this name up; you can call it whatever you want.) In this new file, change DEBUG, etc. The second approach is similar but cuts down on redundancy. Instead of having two settings files whose contents are mostly similar, you can treat one as the base file and create another file that imports from it. For example:


DEBUG = True

DATABASE_ENGINE = 'postgresql_psycopg2'

# ...


from settings import *

DATABASE_NAME = 'production'

Here, imports everything from and just redefines the settings that are particular to production. In this case, DEBUG is set to False, but we’ve also set different database access parameters for the production setting. (The latter goes to show that you can redefine any
setting, not just the basic ones like DEBUG.)

Finally, the most concise way of accomplishing two settings environments is to use a single settings file that branches based on the environment. One way to do this is to check the current hostname. For example:


import socket

if socket.gethostname() == 'my-laptop':

# ...

Here, we import the socket module from Python’s standard library and use it to check the current system’s hostname. We can check the hostname to determine whether the code is being run on the production server. A core lesson here is that settings files are just Python code. They can import from other files, they can execute arbitrary logic, etc. Just make sure that, if you go down this road,
the Python code in your settings files is bulletproof. If it raises any exceptions, Django will likely crash badly.

Feel free to rename your to or settings/ or – Django doesn’t care, as long as you tell it what settings file you’re using.

But if you do rename the file that is generated by django-admin startproject, you’ll find that will give you an error message saying that it can’t find the settings. That’s because it tries to import a module called settings. You can fix this either by editing to change settings to the name of your module, or by using django-admin instead of In the latter case, you’ll need to set the DJANGO_SETTINGS_MODULE environment variable to the Python path to your settings file (e.g., 'mysite.settings').

Deploying Django to a production server

Deploying Django with Apache and mod_wsgi

Deploying Django with Apache and mod_wsgi is a tried and tested way to get Django into production. mod_wsgi is an Apache module which can host any Python WSGI application, including Django. Django will work with any version of Apache which supports mod_wsgi. The official mod_wsgi documentation is fantastic; it’s your source for all the details about how to use mod_wsgi. You’ll probably want to start with the installation and configuration documentation.

Basic Configuration

Once you’ve got mod_wsgi installed and activated, edit your Apache server’s httpd.conf file and add the following. Note, if you are using a version of Apache older than 2.4, replace Require all granted with Allow from all and also add the line Order deny,allow above it.

WSGIScriptAlias / /path/to/
WSGIPythonPath /path/to/

<Directory /path/to/>
Require all granted

The first bit in the WSGIScriptAlias line is the base URL path you want to serve your application at (/ indicates the root url), and the second is the location of a WSGI file – see below – on your system, usually inside of your project package (mysite in this example). This tells Apache to serve any request below the given URL using the WSGI application defined in that file.

The WSGIPythonPath line ensures that your project package is available for import on the Python path; in other words, that import mysite works. The <Directory> piece just ensures that Apache can access your file.

Next we’ll need to ensure this with a WSGI application object exists. As of Django version 1.4, startproject will have created one for you; otherwise, you’ll need to create it.

See the WSGI overview for the default contents you should put in this file, and what else you can add to it.

Using mod_wsgi Daemon Mode

Daemon mode is the recommended mode for running mod_wsgi (on non-Windows platforms). To create the required daemon process group and delegate the Django instance to run in it, you will need to add appropriate WSGIDaemonProcess and WSGIProcessGroup directives.

A further change required to the above configuration if you use daemon mode is that you can’t use WSGIPythonPath; instead you should use the python-path option to WSGIDaemonProcess, for example:

WSGIDaemonProcess python-path=/path/to/\

See the official mod_wsgi documentation for details on setting up daemon mode.