The jobs Nginx owns
- Reverse proxy for .NET services: Kestrel serves the app; Nginx handles the internet — slow clients, header hygiene, connection limits.
- TLS termination: certificates, protocols, and ciphers managed in one place with automated renewal — not per-service.
- WebSocket proxying for OCPP: long-lived charger connections need correct
Upgradehandling and generousproxy_read_timeout— the config error that silently drops chargers every 60 seconds is a rite of passage. - Static assets and Angular apps: immutable cache headers on hashed bundles,
try_filesfallback for SPA routing.
Configuration lessons
- Config lives in git and deploys through the pipeline — a hand-edited nginx.conf on a server is an outage with a delay timer.
proxy_next_upstreamwith care: retrying non-idempotent POSTs on timeout double-charges someone eventually.- Rate limiting (
limit_req) as the first defense layer — cheap protection before traffic touches application code. - Health-check-aware upstreams so deploys drain gracefully — pull a node, connections finish, then it updates.
Why it persists in a cloud world
Managed gateways and load balancers absorb many of these jobs in cloud estates — and I use them there. But on client-hosted deployments, hybrid setups, and cost-sensitive environments, Nginx delivers 90% of that capability for the price of a config file. Knowing it deeply remains one of the highest-leverage infrastructure skills an architect carries.
Worked example: the OCPP-safe server block
The config that keeps long-lived charger WebSockets healthy — note the timeout set above the OCPP heartbeat interval:
upstream ocpp_backend {
least_conn;
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.1.11:8080 max_fails=3 fail_timeout=30s;
}
server {
listen 443 ssl;
server_name ocpp.example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
location /ocpp/ {
proxy_pass http://ocpp_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # WebSocket upgrade
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s; # > heartbeat interval!
proxy_send_timeout 600s;
}
location /api/ {
limit_req zone=partner burst=20 nodelay; # first line of defense
proxy_pass http://api_backend;
}
}
The default 60s proxy_read_timeout is the classic silent charger-disconnect bug — set it above the heartbeat and it disappears.
FAQ
In fully managed cloud estates, use the managed option — less to operate. Nginx wins on client-hosted infrastructure, fine-grained control, and anywhere licensing or egress economics matter.
WebSocket timeouts — default proxy_read_timeout of 60s silently kills idle OCPP connections. Set it above the protocol heartbeat interval and chargers stop mysteriously reconnecting.
It can carry auth, routing, and rate limiting for simpler platforms. Once you need per-partner policies, version negotiation, and developer portals, a dedicated gateway pays for itself.