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:
- Data encryption: Prevents sensitive data from being read if intercepted
- Authentication: Verifies that your users are communicating with your actual server
- Data integrity: Ensures data isn't modified during transmission
- SEO benefits: Search engines favor secure websites
- 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
# 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
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:
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:
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:
sudo apt-get update
sudo apt-get install certbot
On CentOS/RHEL:
sudo yum install certbot
Step 2: Obtain a certificate
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
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"
)
Option 4: Using a Reverse Proxy (Recommended for Production)
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
:
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:
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:
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:
-
Use strong cipher suites: Restrict to modern, secure ciphers
-
Enable HTTP Strict Transport Security (HSTS):
pythonfrom 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 -
Auto-renew certificates: Let's Encrypt certificates expire after 90 days
bash# Add to crontab
0 12 * * * /usr/bin/certbot renew --quiet -
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:
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:
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
- Official FastAPI HTTPS documentation
- Let's Encrypt documentation
- Mozilla SSL Configuration Generator
- SSL Labs Server Test
Exercises
- Generate a self-signed certificate and run your FastAPI application using HTTPS locally.
- Set up a local Nginx server as a reverse proxy to your FastAPI app with SSL termination.
- Create a FastAPI application that enforces HTTPS by redirecting HTTP requests.
- Implement HSTS headers in your FastAPI application to enhance security.
- 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! :)