Heroku is an easy platform to host a Django app on but you should set a few things to make sure your app is secure. Here’s a quick way to do this.
This guide is for Django 3.2.11 (on Python 3.10.2) but it should work fine on 4.0.1 too if you’re not into LTS. It also assumes that you are using Heroku PostgreSQL if you are using a DB.
The easiest way to get your app working is by adding Django-Heroku to it.
This will automatically configure DATABASE_URL, ALLOWED_HOSTS…
Also,
If you set the SECRET_KEY environment variable, it will automatically be used in your Django settings
We can use this feature to our advantage to have an app that works well both in development and securely in production with minimal changes.
Add something like the following to your settings.py
. Change the values if they are not suitable for your app.
if os.environ.get("SECRET_KEY", "") != "":
# HTTP Strict Transport Security - careful now
SECURE_HSTS_SECONDS = os.environ.get("SECURE_HSTS_SECONDS", 0)
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# HTTPS redirects
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = (
"HTTP_X_FORWARDED_PROTO", # Heroku strips and sets X-Forwarded-Proto correctly
"https",
)
# Secure cookies
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# Only run in debug mode if local
DEBUG = False
This enables security settings if SECRET_KEY
is specified. The app won’t start if it is actually an empty string and the insecure prefixed one from the default template will show a warning when we run the deployment check. SECURE_HSTS_SECONDS
is set separately as this can cause problems if set incorrectly and a value of 0 also disables the other HSTS flags.
Now we need to generate a secret key for production. This easiest way to do this is the same way Django does. Open a Django shell:
./manage.py shell
Then run the following:
from django.core.management import utils
utils.get_random_secret_key()
This is equivalent to the following:
from django.utils import crypto
crypto.get_random_string(50, "abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)")
The first param (length) is required from Django 4.0 onwards.
If you don’t want to use a Django shell or you are using an old version then you can go a level deeper and open a python shell:
python3
Then run:
import secrets
secrets.token_urlsafe(100)
Then open the web interface for your app and go to Settings > Config Vars. Click the “Reveal Config Vars” button and add SECRET_KEY
with the value you generated.
Set SECURE_HSTS_SECONDS
to 3600 to start and increase later. Leave DATABASE_URL
and other settings as-is.
Then run a deployment check and make sure that there are no warnings:
heroku run -a <YOUR APP NAME> ./manage.py check --deploy
It is also worth checking that you are running the latest patch versions of Django and Python.
heroku run -a <YOUR APP NAME> ./manage.py --version
heroku run -a <YOUR APP NAME> python --version
If your requirements.txt
looks something like the following then Django should auto-update every time you deploy.
django>=3.2.10,<3.3
psycopg2
django-heroku
gunicorn
To update Python you may need to change your runtime.txt
file:
python-3.10.2
If you need to perform any actions before a deployment then you can do this with an entry at the top of your Procfile
. For example, to automatically run any Django database migrations you could put this as the first line.
release: python manage.py migrate
Other things to consider are setting up Multi-Factor Authentication. I like to use the andOTP - Android OTP Authenticator app.
You can also make sure your SSH Keys are in shape. I prefer elliptic-curve (EC) e.g. ed25519
to RSA.