Skip to main content

Nginx reverse proxy

Nginx terminates TLS and forwards traffic to localhost Python services. Only Nginx listens on ports 80 and 443.

Subdomain routing (preferred)

Each API gets its own subdomain:

customer.example.com  → 127.0.0.1:8001
reports.example.com → 127.0.0.1:8002

Advantages: clean URLs, simpler CORS, independent SSL SAN entries, easier OpenAPI at /docs.

File locations

FilePurpose
/etc/nginx/sites-available/<api-name>Server block definition
/etc/nginx/sites-enabled/<api-name>Symlink to sites-available
/etc/nginx/nginx.confGlobal settings (usually leave default)

Enable a site:

sudo ln -sf /etc/nginx/sites-available/<api-name> /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

HTTP-only (LAN testing)

Use default_server on port 80 so http://<lan-ip>/health and /docs work without DNS.

Full runbook: 11-nginx-lan-setup.md
Template: templates/nginx/dwd-api-fastapi-lan.conf

For subdomain production, use templates/nginx-site.template.

HTTPS with existing certificate

Add to the server block (paths depend on your cert provider):

listen 443 ssl;
listen [::]:443 ssl;

ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;

# Recommended baseline
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;

Redirect HTTP to HTTPS:

server {
listen 80;
listen [::]:80;
server_name <subdomain>;
return 301 https://$host$request_uri;
}

Record actual certificate paths here when configured:

ItemPath
Full chainTBD
Private keyTBD

Proxy headers

The template sets headers expected by most Python frameworks:

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;

FastAPI/Starlette apps behind HTTPS should trust forwarded proto if generating absolute URLs — often via middleware or root_path when needed.

WebSockets

If an API uses WebSockets, the template includes:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Timeouts

Default Nginx proxy timeouts may be too short for long-running requests. Adjust in the location block if needed:

proxy_read_timeout 300s;
proxy_send_timeout 300s;

Verification

sudo nginx -t
curl -I http://127.0.0.1/ -H "Host: <subdomain>"
curl -I https://<subdomain>/health

Multiple APIs

Each API gets its own file under sites-available/. Do not combine unrelated APIs into one server block unless using path-based routing (not recommended for this server).

Path-based alternative (not default here):

location /customer/ {
proxy_pass http://127.0.0.1:8001/;
}

Requires application URL prefix configuration and is harder to maintain than subdomains.