Serving HTTPS

Pass a certificate and its private key. Uvicorn builds the SSLContext from the ssl_* parameters, all listed in the parameters reference. In most production setups a reverse proxy terminates TLS instead.

import uvicorn

if __name__ == "__main__":
    uvicorn.run(
        "app.main:app",
        host="0.0.0.0",
        port=8443,
        ssl_keyfile="./ssl/key.pem",
        ssl_certfile="./ssl/cert.pem",
        ssl_keyfile_password=None,  # set it if the key is encrypted
    )

Client Certificates (Mutual TLS)

Require clients to present a certificate signed by your CA with ssl_cert_reqs and ssl_ca_certs.

import ssl

import uvicorn

if __name__ == "__main__":
    uvicorn.run(
        "app.main:app",
        host="0.0.0.0",
        port=8443,
        ssl_keyfile="./ssl/key.pem",
        ssl_certfile="./ssl/cert.pem",
        ssl_cert_reqs=ssl.CERT_REQUIRED,  # reject clients without a valid certificate
        ssl_ca_certs="./ssl/client-ca.pem",  # CA that signed the client certificates
    )

Custom SSL Context

Since uvicorn 0.47.0, ssl_context_factory covers what the ssl_* parameters can't: a minimum TLS version, custom SSLContext.options, certificates loaded from memory. It receives the Config and a default factory that builds the usual context. The factory runs in each worker process, so with reload or workers it must be picklable (a top-level function, not a lambda).

import ssl
from collections.abc import Callable

import uvicorn
from uvicorn.config import Config


# Must be a top-level function so it can be pickled with reload/workers
def ssl_context_factory(config: Config, default_ssl_context_factory: Callable[[], ssl.SSLContext]) -> ssl.SSLContext:
    context = default_ssl_context_factory()  # built from the ssl_* parameters
    context.minimum_version = ssl.TLSVersion.TLSv1_3
    return context


if __name__ == "__main__":
    uvicorn.run(
        "app.main:app",
        ssl_keyfile="./ssl/key.pem",
        ssl_certfile="./ssl/cert.pem",
        ssl_context_factory=ssl_context_factory,
    )

Experimental HTTP/2

Since uvicorn 0.53.0, the zttp HTTP implementation can serve HTTP/2 with http2=True. Each connection is served as HTTP/1.1 or HTTP/2 depending on what the client speaks. Uvicorn 0.54.0 added response trailers and 103 Early Hints (needs zttp>=0.0.34). Upgrade-based h2c and WebSockets over HTTP/2 are not supported, and it is not ready for production traffic yet.

# pip install "uvicorn" "zttp>=0.0.34"
import uvicorn

if __name__ == "__main__":
    uvicorn.run(
        "app.main:app",
        http="zttp",  # experimental, not for production traffic yet
        http2=True,  # serves HTTP/1.1 and HTTP/2, depending on the client
    )