How to Django, Certbot — HTTPS Subdomains
This article will go through how I got subdomains to work on Django 3 with Certbot for https. It was a little finicky to get working, since I couldn’t find much info on some specific errors I was running into. Maybe I’m just not a high level search engine ninja, but I figured it out myself. My server was using nginx, but I think it shouldn’t matter if you use apache or something else. There are 3 components to get subdomains to work on Django with https: modifying domain records, using subdomains
package for Django, and adding the subdomain to your certificate with certbot
.
A/AAAA + CNAME Record
To open the subdomain name, we need to add an A and CNAME record to our domain. So go to your hosting service’s dashboard to create these records:
A/AAAA
Hostname: subdomain
IP Address: your_server_ipCNAME
Hostname: *.subdomain
Aliases to: subdomain.example.com
pip install subdomains
When searching for “django subdomains”, you’ll probably come across a package called django-subdomains
https://django-subdomains.readthedocs.io/en/latest/. You’ll proceed to look at the date of the post, check the source code repo, and be disappointed to see that it hasn’t been updated since nearly 2 versions of Django prior. Luckily for us, the beauty of open source means that there must be a fork it being maintained.
pip install subdomains
https://github.com/abe312/django-subdomains. As of writing this, subdomains
is working with Django 3.
Setup: subdomains
require you to configure the app’s settings, the urlpatterns, and database object values correctly.
# subdomains settings: project/project/settings.py
INSTALLED_APPS = [
...
"django.contrib.sites",
"subdomains",
]# subdomains line needs to go before CommonMiddleware
MIDDLEWARE = [
...
"subdomains.middleware.SubdomainURLRoutingMiddleware",
'django.middleware.common.CommonMiddleware',
...
]# values are app's path
ROOT_URLCONF = "project.urls"
SUBDOMAIN_URLCONFS = {
None: "project.urls",
"subdomain": "subdomain_app.urls"
}
SITE_ID = 1
Next, we need to configure the route in your urls.py of the project. Take note that there is no slash for the subdomain path.
# urlpatterns: project/project/urls.py
from django.urls import path, include
urlpatterns = [
...
path(r"subdomain", include("subdomain_app.urls")),
]
Lastly, from the setup in subdomains
docs, we need to set the Site object in our database.
> python manage.py makemigrations
> python manage.py migrate
> python manage.py shell
from django.contrib.sites.models import Site
one = Site.objects.all()[0]
one.domain = 'example.com'
one.name = 'example'
one.save()
Subdomains Development and Production
I handle development and production values with an environment variable. Here is what is in my project/project/settings.py
to change values depending if i’m developing or in production:
# settings.py
prj_env = "dev" # Put your env var with your favorite methodif prj_env == "dev":
DEBUG = True
ALLOWED_HOSTS = ["localhost", "subdomain.localhost"]
else:
DEBUG = False
ALLOWED_HOSTS = ["example.com", "www.example.com", "subdomain.example.com", "www.subdomain.example.com"]
This should be all we need for Django to point a subdomain request to the correct view. At this point, development on localhost should be working. Follow the next steps to get it working on production.
Certbot
If you’re in my situation where you already have a registered cert, I got good news for you. There is a flag for appending to the current certificate. Check your current certs with sudo certbot certificates
, and it will be on the Domains: domain1 domain2
line. You need to run certbot -d domains
with all of the current domains, append your new subdomain(s) at the end, and lastly add the --expand
flag. It should look something like this:
# on prod server
> certbot -d domain1,domain2,new_subdomain --expand
# restart nginx, i'm using ubuntu here
> sudo service nginx restart
Subdomains should be working now. I’d like to hear in the comments if there is any better way to handle subdomains in Django.