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
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.
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:
or from a file:
You must never enable debug in production.
When we created a project in Chapter 1, the command
django-admin startproject created a
settings.py 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
- 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
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 = 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!
EMAIL_BACKEND and Related Settings
If your site sends emails, these values need to be set correctly.
STATIC_ROOT and STATIC_URL
Static files are automatically served by the development server. In production, you must define a
STATIC_ROOT directory where
collectstatic will copy them.
MEDIA_ROOT and MEDIA_URL
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.
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.
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 and MANAGERS
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:
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:
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:
Make sure you give the correct path to your virtualenv, and replace
python3.X with the correct Python version (e.g.
Using Different Settings for Production
So far in this book, we’ve dealt with only a single settings file: the
settings.py 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
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
settings.py. Now, just make a copy of it called
settings_production.py. (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:
settings_production.py imports everything from
settings.py 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
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:
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
foobar.py – Django doesn’t care, as long as you tell it what settings file you’re using.
But if you do rename the
settings.py file that is generated by
django-admin startproject, you’ll find that
manage.py 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
manage.py to change
settings to the name of your module, or by using
django-admin instead of
manage.py. 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.,
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.
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.
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.
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
Next we’ll need to ensure this
wsgi.py 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
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:
See the official
mod_wsgi documentation for details on setting up daemon mode.