In production, Nginx sits in front of all Nadoo AI services as a reverse proxy. It handles TLS termination, WebSocket upgrades, SSE streaming, rate limiting, and routes traffic to the correct backend or frontend service.
Below is the production nginx.conf used by Nadoo AI. It handles HTTP-to-HTTPS redirect, WebSocket proxy, SSE streaming, rate limiting, and security headers.
1
HTTP Server (Port 80)
The HTTP server handles Let’s Encrypt ACME challenges and redirects all other traffic to HTTPS:
WebSocket connections for real-time chat must be proxied with the Upgrade and Connection headers. This location block must appear before the general /api block:
The Strict-Transport-Security header with max-age=31536000 tells browsers to always use HTTPS for the next year. Only enable this after verifying your TLS setup is correct — removing it requires waiting for the max-age to expire.
# Rate limiting zone definitionslimit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;limit_req_zone $binary_remote_addr zone=general_limit:10m rate=100r/s;limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=1r/s;# Connection limiting - max 20 concurrent connections per IPlimit_conn_zone $binary_remote_addr zone=conn_limit:10m;limit_conn conn_limit 20;# Return 429 Too Many Requests (not the default 503)limit_req_status 429;limit_conn_status 429;
The auth_limit zone at 1 request per second with a burst of 5 provides effective brute-force protection for login and registration endpoints without impacting normal usage.
Ensure the /api/ws location block appears before the /api block in your Nginx config. Nginx uses first-match for prefix locations, and a generic /api match will not include the WebSocket upgrade headers.Verify with:
SSE responses arrive in chunks instead of streaming
Confirm that proxy_buffering off and gzip off are set for the SSE location blocks. Also check that no upstream proxy (e.g., a CDN or load balancer) is buffering responses.Test SSE streaming directly: