Django like all modern application development frameworks requires that you eventually manage tasks to support the core operation of a project. This can range from efficiently setting up a Django application to run in the real world, deploying a Django application to a cloud provider, to managing an application's static resources (e.g. CSS, JavaScript, image files).
In addition, other routine application management tasks can include: establishing a logging strategy to enforce problem detection; setting up email delivery for application users and/or administrators; as well as debugging tasks to inspect the outcome of complex operations. In this chapter, you'll learn about these and other common topics associated with Django application management.
Django settings.py for the real world
The settings.py
is
the central configuration for all Django projects. In previous
chapters you already worked with a series of variables in this file
to configure things like Django applications, databases, templates
and middleware, among other things.
Although the
settings.py
file uses reasonable default values for
practically all variables, when a Django application transitions
into the real world, you need to take into account a series of
adjustments, to efficiently run the Django application, offer end
users a streamlined experience and keep potentially rogue attackers
in check.
Switch DEBUG to False
One of the first things that's
necessary to launch a Django application into the real world is to
change the DEBUG
variable to False
in a project's settings.py
file. I've
briefly mentioned in previous chapters how Django's behavior
changes when switching DEBUG=False
to
DEBUG=True
in settings.py
. All these behavioral changes associated
with the DEBUG
variable are intended to enhance
project security and performance. Table 5-1 illustrates the differences between
having a project run with DEBUG=False
and
DEBUG=True
.
Table 5-1. Django behavior differences between DEBUG=True and
DEBUG=False in settings.py
Functionality | DEBUG=True behavior | DEBUG=False behavior |
---|---|---|
Error handling and notification | Displays full stack of errors on request pages for quick analysis | Displays default 'vanilla' or custom error pages without any stack details to limit security threats or embarrassments. Emails project administrators of errors.(See the 'Define administrators for ADMINS and MANAGERS' section in this section for more details on email notifications) |
Static resources | Set up by default on a project's /static/ URL for simplicity. | Disables automatic set up to enhance performance and avoid security
vulnerabilities. This requires dealing with static resources in a separate workflow, using one of various techniques:
The upcoming section Set up static web page resources -- Images, CSS, JavaScript -- has more details |
Host/site qualifier | Requests for all hosts/sites are accepted for processing | It's necessary to qualify for which hosts/sites a project can handle requests. If a site/host is not qualified, all requests are denied. (See the 'Define ALLOWED_HOSTS' sub-section in this section for more details) |
As you can see in table 5-1, the
changes enforced by changing DEBUG=True
to
DEBUG=False
in settings.py
are intended for publicly accessible
applications (i.e. production environments). You may not like the
hassle of adapting to these changes, but they are enforced to
maintain a heightened level of security and maintain high performance on all Django projects that
run in the real world.
Define ALLOWED_HOSTS
By default, the
ALLOWED_HOSTS
variable in settings.py
is
empty. The purpose of ALLOWED_HOSTS
is to validate a
request's HTTP Host
header. Validation is done to
prevent rogue users from sending fake HTTP Host
headers that can potentially poison caches and password reset
emails with links to malicious hosts. Since this issue can only
present itself under an uncontrolled user environment (i.e.
public/production servers), this validation is only done when
DEBUG=False
.
If you switch to
DEBUG=False
and ALLOWED_HOSTS
is left
empty, Django refuses to serve requests and instead responds with
HTTP 400 bad request pages, since it can't validate incoming HTTP
Host
headers. Listing 5-1 illustrates a sample
definition of ALLOWED_HOSTS
.
Listing 5-1 Django ALLOWED_HOSTS definition
ALLOWED_HOSTS = [
'.coffeehouse.com',
'.bestcoffeehouse.com',
]
As you can see in listing 5-1,
the ALLOWED_HOSTS
value is a list of strings. In this
case it defines two host domains, that allow
bestcoffeehouse.com
to act as an alias of
coffeehouse.com
. The leading .(dot) for each domain
indicates a sub-domain is also an allowed host domain (e.g.
static.coffeehouse.com
or
shop.coffeehouse.com
is valid for
.coffeehouse.com
).
If you want to accept a single
and fully qualified domain (FQDN) you would define
ALLOWED_HOSTS=['www.coffeehouse.com']
, which would
only accept requests with an HTTP Host
www.coffeehouse.com
. In a similar fashion, if you
want to bypass this security feature and accept any HTTP host value in a request -- which I don't recommend, see below -- you can define ALLOWED_HOSTS=['*']
, where '*'
represents a wild-card to accept any HTTP host value.
Caution Although setting ALLOWED_HOSTS=['*']
offers a quick solution, doing so raises the possibility of the rare, but possible, security vulnerability: HTTP host header attack[1].
Be careful with the SECRET_KEY value
The SECRET_KEY
value
in settings.py
is another security related variable
like ALLOWED_HOSTS
. However, unlike
ALLOWED_HOSTS
, SECRET_KEY
is assigned a
default value and a very long value at that (e.g.
'django-insecure-3*w@re!%88p%w%+-3^g_z=pna5zot51cfjt4t^!6=u7sp7qo1!'
).
The purpose of the SECRET_KEY
value is to digitally sign certain data
structures that are sensitive to tampering. Specifically, Django by
default uses the SECRET_KEY
on sensitive data
structures like session identifiers, cookies and password reset
tokens. But you can rely on the SECRET_KEY
value to
cryptographically protect any sensitive data structure in a Django project[2].
The remaining content for Django 4.0 is only available with a subscription.