Skip to main content

FastAPI SSL/TLS Setup

Introduction

When deploying a FastAPI application to production, securing the data transmission between clients and your server is critical. This is where SSL/TLS certificates come in, enabling HTTPS connections that encrypt data in transit.

In this guide, we'll explore how to set up SSL/TLS for your FastAPI applications, ensuring that your API endpoints are accessed securely via HTTPS instead of plain HTTP. We'll cover both development and production environments, and walk through different methods for implementing this important security layer.

Why Use SSL/TLS?

Before diving into implementation, let's understand why SSL/TLS is important:

  1. Data encryption: Prevents sensitive data from being read if intercepted
  2. Authentication: Verifies that your users are communicating with your actual server
  3. Data integrity: Ensures data isn't modified during transmission
  4. SEO benefits: Search engines favor secure websites
  5. Modern browser requirements: Many browsers mark non-HTTPS sites as "Not Secure"

Prerequisites

Before we begin, make sure you have:

  • A working FastAPI application
  • Python 3.7+
  • Basic understanding of HTTP/HTTPS protocols
  • Access to a terminal/command line

SSL/TLS Setup Options

Option 1: Local Development with Self-Signed Certificates

For development purposes, you can create self-signed certificates. These won't be trusted by browsers but are perfect for testing.

Step 1: Generate a self-signed certificate

bash
# Create a directory for certificates
mkdir certs
cd certs

# Generate a private key and self-signed certificate
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

When prompted, fill in the details or press Enter for defaults. The -nodes option ensures the private key isn't encrypted with a passphrase.

Step 2: Update your FastAPI application to use SSL/TLS

python
import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello, secure world!"}

if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8443,
ssl_keyfile="./certs/key.pem",
ssl_certfile="./certs/cert.pem"
)

Now run your application:

bash
python main.py

Your FastAPI application is now running at https://localhost:8443. When you access it in a browser, you'll see a warning because the certificate is self-signed. You can proceed by accepting the risk (this is safe for local development).

Option 2: Using Uvicorn's Built-in SSL Support

If you run your application using Uvicorn directly, you can specify SSL certificates as command-line parameters:

bash
uvicorn main:app --host 0.0.0.0 --port 8443 --ssl-keyfile ./certs/key.pem --ssl-certfile ./certs/cert.pem

This approach is equivalent to the previous example but doesn't require modifying your code.

Option 3: Production Setup with Let's Encrypt

For production environments, you should use certificates from a trusted Certificate Authority (CA). Let's Encrypt offers free certificates that are trusted by browsers.

Step 1: Install Certbot

On Ubuntu/Debian:

bash
sudo apt-get update
sudo apt-get install certbot

On CentOS/RHEL:

bash
sudo yum install certbot

Step 2: Obtain a certificate

bash
sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com

This creates certificates in /etc/letsencrypt/live/yourdomain.com/.

Step 3: Configure FastAPI with the certificates

python
import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello, secure world!"}

if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=443,
ssl_keyfile="/etc/letsencrypt/live/yourdomain.com/privkey.pem",
ssl_certfile="/etc/letsencrypt/live/yourdomain.com/fullchain.pem"
)

In production environments, it's common to use a reverse proxy like Nginx to handle SSL/TLS termination. This offloads encryption tasks from your application server.

Step 1: Set up Nginx with SSL

Create an Nginx configuration file /etc/nginx/sites-available/fastapi:

nginx
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;

# Redirect HTTP to HTTPS
location / {
return 301 https://$host$request_uri;
}
}

server {
listen 443 ssl;
server_name yourdomain.com www.yourdomain.com;

ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

# Modern SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;

# Proxy to FastAPI
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Enable the configuration and restart Nginx:

bash
sudo ln -s /etc/nginx/sites-available/fastapi /etc/nginx/sites-enabled/
sudo nginx -t # Test the configuration
sudo systemctl restart nginx

Step 2: Run your FastAPI application without SSL

Since Nginx handles SSL/TLS termination, your FastAPI application can run on HTTP:

bash
uvicorn main:app --host 127.0.0.1 --port 8000

SSL/TLS Best Practices

When implementing SSL/TLS for your FastAPI applications, consider these best practices:

  1. Use strong cipher suites: Restrict to modern, secure ciphers

  2. Enable HTTP Strict Transport Security (HSTS):

    python
    from fastapi import FastAPI, Request, Response

    app = FastAPI()

    @app.middleware("http")
    async def add_hsts_header(request: Request, call_next):
    response = await call_next(request)
    response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
    return response
  3. Auto-renew certificates: Let's Encrypt certificates expire after 90 days

    bash
    # Add to crontab
    0 12 * * * /usr/bin/certbot renew --quiet
  4. Test your SSL setup: Use tools like SSL Labs to check your configuration

Handling Certificate Verification

When making requests to other HTTPS services from your FastAPI app, you might need to handle certificate verification:

python
import httpx

# Secure by default (verifies certificates)
async def secure_request():
async with httpx.AsyncClient() as client:
response = await client.get("https://api.example.com/data")
return response.json()

# For development only - disable verification (NOT recommended for production)
async def insecure_request():
async with httpx.AsyncClient(verify=False) as client:
response = await client.get("https://api.example.com/data")
return response.json()

Real-World Example: Secure API with API Keys

Here's an example that combines SSL/TLS with API key authentication:

python
import uvicorn
from fastapi import FastAPI, Security, Depends, HTTPException
from fastapi.security.api_key import APIKeyHeader
from starlette.status import HTTP_403_FORBIDDEN

app = FastAPI(title="Secure API")

API_KEY = "your-secret-api-key"
API_KEY_NAME = "X-API-Key"

api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False)

async def get_api_key(api_key_header: str = Security(api_key_header)):
if api_key_header == API_KEY:
return api_key_header
else:
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail="Invalid API Key"
)

@app.get("/secure-data", dependencies=[Depends(get_api_key)])
async def get_secure_data():
return {"data": "This is protected data!"}

if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8443,
ssl_keyfile="./certs/key.pem",
ssl_certfile="./certs/cert.pem"
)

Summary

Setting up SSL/TLS for your FastAPI application is crucial for securing data transmission between clients and your server. We've covered:

  • Creating self-signed certificates for development
  • Using Uvicorn's built-in SSL support
  • Setting up Let's Encrypt certificates for production
  • Implementing a reverse proxy with Nginx (recommended for production)
  • Best practices for SSL/TLS security

By following these guidelines, you can ensure that your FastAPI applications are secure and protect your users' data from potential threats.

Additional Resources

Exercises

  1. Generate a self-signed certificate and run your FastAPI application using HTTPS locally.
  2. Set up a local Nginx server as a reverse proxy to your FastAPI app with SSL termination.
  3. Create a FastAPI application that enforces HTTPS by redirecting HTTP requests.
  4. Implement HSTS headers in your FastAPI application to enhance security.
  5. Set up automatic certificate renewal for a Let's Encrypt certificate.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)