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.