Willy's blog

Local HTTPS development in Python with Mkcert

About mkcert

mkcert allows you to have a local certificate authority (CA). This means that you can run your development web server using HTTPS. You'll see the green lock in your browser.

You might not need it most of the time, but more and more features require HTTPS by default in the browser, like web bluetooth, service workers, web authentication and websockets in some cases where SSL is already enabled.

Configuring mkcert

Install the dependencies for your os. In my case I'm using a Linux Debian based os.

sudo apt install libnss3-tools

Download the binary for your distribution from mkcert release page

Rename the file to mkcert and give execution permissions

chmod +x mkcert

Install the certificate authorities (CA) using

./mkcert -install

Create certificates for the domains you'd like to use

./mkcert -cert-file cert.pem -key-file key.pem 0.0.0.0 localhost 127.0.0.1 ::1

This will create a cert.pem and a key.pem files in your current directory. We are gonna use this 2 files along the post.

Python frameworks

Now that we have created the certficates for those domains, we just need to know how to tell our app to use them.

I'll assume you have a virtualenv already created.

Let's see how to do this in some different python frameworks and tools.

Uvicorn + Starlette

Update: uvicorn now has SSL support.

I've used starlette because it's simpler than writing a uvicorn App, but it's not required.

I guess this should be fairly similar for FastAPI, Bocadillo and Responder.

In your terminal run

pip install uvicorn starlette

Now create a file star_app.py

# star_app.py
from starlette.applications import Starlette
from starlette.responses import JSONResponse
import uvicorn
import ssl

app = Starlette()


@app.route("/")
async def homepage(request):
    return JSONResponse({"hello": "world"})

if __name__ == "__main__":
    uvicorn.run(
        app,
        host="0.0.0.0",
        port=8433,
        ssl_version=ssl.PROTOCOL_SSLv23,
        cert_reqs=ssl.CERT_OPTIONAL,
        ssl_keyfile="./key.pem",        # Note that the generated certificates
        ssl_certfile="./cert.pem",      # are used here
    )

And then just run

python star_app.py

Go to https://0.0.0.0:8443 in your browser

Django SSL Server

django-sslserver is a small library which adds the ability to run a secure debug server with the certificates we just created.

pip install django-sslserver

Update your settings.py

INSTALLED_APPS = (...
    'sslserver',
    ...
)

And in your terminal run

python manage.py runsslserver --certificate cert.pem --key key.pem

Django extensions

There's another alternative which I haven't tested, but it has a lot of extra functionality, which I don't need, so I've skipped it.

Feel free to try django-extensions

I guess it would be something like

python manage.py runserver_plus --cert-file cert.pem --key-file cert.pem

Flask

Install flask

pip install flask

Create a file flask_app.py

# flask_app.py
from flask import Flask

application = Flask(__name__)

@application.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
    application.run(ssl_context=('cert.pem', 'key.pem'))

Run in your terminal

python flask_app.py

Gunicorn

Install gunicorn.

pip install gunicorn

Let's continue with the previous flask example

gunicorn flask_app:application --keyfile=./key.pem --certfile=./cert.pem

UWSGI

Install uwsgi.

pip install uwsgi

Create a file called wsgi.py

# wsgi.py
def application(env, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b"Hello World"]

Run in your terminal

uwsgi -w wsgi --https=0.0.0.0:8443,cert.pem,key.pem

Go to https://0.0.0.0:8443

Security concerns

DO NOT use this certificates in production. This is only for development. Use Let's Encrypt instead.

You don't need to commit the generated certificates. Looks like each machine will have to install mkcert, create and work with its own certificates.

This will only work on your local machine, where the server is running, if you want to access from a mobile device read the docs.

Conclusion

Many times, I've had the need to test something with HTTPS, but it took me a lot of time to do it. I think mkcert is a really easy to use tool which achieves this smoothly.

Do you have any other (security) concerns? Feedback is appreciated.

If you have drop-in examples for other frameworks or tools I'll update the post.

Thanks for reading and happy coding!

Edit: gunicorn example + uvicorn now supports ssl.