INFRASTRUCTURE GUIDE · UPDATED 11 AUG 2026

Nginx: the quiet workhorse at the edge

Before traffic reaches any Kestrel process on my platforms, it passes through Nginx. TLS, compression, WebSocket upgrades for OCPP, static assets, rate limits — handled by the most boring, reliable piece of software in the stack.

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 Upgrade handling and generous proxy_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_files fallback 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_upstream with 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

Nginx vs cloud load balancers?

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.

The mistake you see most?

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.

Nginx as an API gateway?

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.