DEV Community

Cover image for HTTPS for Django Development Environment
Aidas Bendoraitis
Aidas Bendoraitis

Posted on • Originally published at djangotricks.com

HTTPS for Django Development Environment

Certain modern website features require HTTPS, including Progressive Web Apps (PWA), WebSockets, camera and microphone usage, and geolocation detection. However, the default Django runserver command can only run the web server under HTTP. Is it possible to have HTTPS in your local development environment? Surely, and in this article, I will guide you through how to set it up.

How TLS/SSL certificates work

First of all, let's have a common ground on how the HTTPS work.

HTTPS is what makes websites secure, protecting transferred visitors' data (like passwords or credit card info).

TLS (Transport Layer Security), which replaced SSL (Secure Sockets Layer), is the security tool that works behind the scenes to create the safe connection between a client (like a browser) and a server by encrypting the data exchanged.

They work using a public key (shared via the certificate) and a private key (kept secret by the server) to establish encrypted connections. These keys are often stored in BASE64-encoded files with extensions like .pem.

A Certificate Authority (CA) is a trusted organization or entity responsible for issuing and managing digital certificates used in TLS/SSL encryption. Trusted Certificate Authorities verify the identity of website owners and sign their certificates to ensure authenticity.

A trusted Certificate Authority signs the certificate to verify the server’s identity, and clients (like the browsers) validate it against the Certificate Authority’s root certificate to ensure the connection is authentic and secure. The CA root certificates are typically stored in the operating system or the browser and updated regularly with software updates.

Some well-known Certificate Authorities include:

  • Let’s Encrypt (free, automated certificates),
  • DigiCert,
  • GlobalSign,
  • Sectigo (formerly Comodo).

For local development, one can use a simple tool mkcert to create locally trusted TLS certificates to secure development environments without needing external Certificate Authorities. Soon, you'll see how to do that.

TLS/SSL certificates are used not only for HTTPS connections but also to secure other communication protocols, including WebSockets (wss://), FTPS, database connections, email protocols (SMTP, IMAP, POP3), and message brokers (e.g., Redis, RabbitMQ, Kafka).

1. The /etc/hosts file

I usually create local hostnames for my development environments to access them by domains, such as djangotricks.local instead of localhost or 127.0.0.1. This can be done by adding a mapping line to your /etc/hosts file:

127.0.0.1 djangotricks.local
Enter fullscreen mode Exit fullscreen mode

2. Install mkcert

Let's install mkcert to your development environment. I used Homebrew with the following commands on macOS:

$ brew install mkcert
$ brew install nss  # for Firefox
$ mkcert -install
Enter fullscreen mode Exit fullscreen mode

Installation instructions for other operating systems are available at the mkcert GitHub page.

3. Create certificates for your local domain

I made a directory certs in my Django project, changed the current directory to it, and then ran:

mkcert djangotricks.local
Enter fullscreen mode Exit fullscreen mode

Afterwards, I created .gitignore file in it with the following content that ignores everything but itself:

*
!.gitignore
Enter fullscreen mode Exit fullscreen mode

Finally, my project's file structure looked like this:

djangotricks/
├── certs/
│   ├── .gitignore
│   ├── djangotricks.local.pem  - public certificate
│   ├── djangotricks.local-key.pem  - private key
├── manage.py
Enter fullscreen mode Exit fullscreen mode

4. Django settings

I added a few TLS-related settings to my development-environment settings file at djangotricks/settings/dev.py:

CSRF_TRUSTED_ORIGINS = [
    "https://djangotricks.local:8000",
    "http://djangotricks.local:8000",
]

CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
Enter fullscreen mode Exit fullscreen mode

5.a) Create a custom script for the WSGI server

The next step was to prepare scripts for running the web server under HTTPS. After a few iterations of chatting with Claude AI, I created a file run_https_wsgi_server.py with the following content:

import os
import ssl
from pathlib import Path
from django.core.servers.basehttp import get_internal_wsgi_application
from wsgiref.simple_server import make_server
from watchfiles import run_process

def run_server():
    # Set Django environment
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangotricks.settings.dev")
    application = get_internal_wsgi_application()

    # Set certificate paths
    cert_path = Path(__file__).parent / "certs"
    ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ssl_context.load_cert_chain(
        certfile=cert_path / "djangotricks.local.pem",
        keyfile=cert_path / "djangotricks.local-key.pem",
    )

    # Run server with TLS
    with make_server("djangotricks.local", 8000, application) as httpd:
        httpd.socket = ssl_context.wrap_socket(httpd.socket, server_side=True)
        print("Serving on https://djangotricks.local:8000")
        try:
            httpd.serve_forever()
        except KeyboardInterrupt:
            print("Server stopped.")

if __name__ == "__main__":
    # Watch directories for changes
    watched_dirs = [
        str(Path(__file__).parent), # Current directory
    ]

    # Start watching and run the server with auto-reload
    run_process(
        *watched_dirs,            # Paths to watch
        target=run_server,        # Function to run
        debounce=1600,            # Debounce changes (milliseconds)
        recursive=True,           # Watch directories recursively
        debug=True                # Enable debug logging
    )    
Enter fullscreen mode Exit fullscreen mode

This Python script runs Python's default web server with the Django WSGI under HTTPS, watches the project files for changes, and restarts the server whenever something is updated.

Then I installed the dependency watchfiles, which is a Rust-powered watcher for file changes:

(venv)$ pip install watchfiles
Enter fullscreen mode Exit fullscreen mode

That allowed me to run the web server script with:

(venv)$ python run_https_wsgi_server.py
Enter fullscreen mode Exit fullscreen mode

5.b) Create a custom script for the ASGI server

Analogically, I created a script for running the web server under HTTPS with ASGI support. It uses uvicorn for the ASGI protocol and watchfiles for automatic reloading:

(venv)$ pip install uvicorn
(venv)$ pip install watchfiles
Enter fullscreen mode Exit fullscreen mode

I named the script run_https_asgi_server.py and it had the following content:

import os
from pathlib import Path
import uvicorn

if __name__ == "__main__":
    # Set Django environment
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangotricks.settings.dev")

    # Set certificate paths
    project_root = Path(__file__).parent
    ssl_keyfile = project_root / "certs" / "djangotricks.local-key.pem"
    ssl_certfile = project_root / "certs" / "djangotricks.local.pem"

    # Run server with TLS
    uvicorn.run(
        "djangotricks.asgi:application",
        host="djangotricks.local",
        port=8000,
        ssl_keyfile=str(ssl_keyfile),
        ssl_certfile=str(ssl_certfile),
        reload=True,  # Enable autoreload
        reload_dirs=[
            str(project_root),
        ],
        reload_includes=["*.py", "*.html", "*.js", "*.css", "*.txt"],
        workers=1,
    )
Enter fullscreen mode Exit fullscreen mode

Uvicorn's core features include integration of watchfiles and TLS/SSL support. Developers can define which directories and file types to watch for autoloading.

Lastly, I could run the script with:

(venv)$ python run_https_asgi_server.py
Enter fullscreen mode Exit fullscreen mode

6. Test your server

To test my results, I navigated to https://djangotricks.local:8000, browsed all the main pages, tried to submit a form, tried an asynchronous view, tried one of the modern JavaScript features, and tried changing some HTML markup to see how the web server automatically restarts for the changes to take effect immediately.

Conclusion

HTTPS is an essential feature of modern web development, and it's absolutely necessary to be able to set it up in development environments.

Tools like mkcert make it possible to use TLS certificates locally.

Although Django's runserver doesn't yet support TLS/SSL certificates, you can create custom Python (or Bash) scripts to run the web servers with HTTPS and autoloading. Also, it is quite straightforward to remove the autoloading if you don't need it.

Now, it's time for me to update all my local development environments. For you, too?


Cover picture by Laura Gigch

Top comments (0)