HTTP Fundamentals Best Practices
A condensed summary of the 25 most important HTTP practices for Node.js backends - drawn from every page in this section.
Search across all documentation pages
A condensed summary of the 25 most important HTTP practices for Node.js backends - drawn from every page in this section.
Set requestTimeout on every server: 30s is a sane default - HTTP Basics in Node.
Set headersTimeout > requestTimeout: Node 18+ enforces this relationship or connections break mid-request.
Align keepAliveTimeout below proxy idle timeout: Prevents 502s during rolling deploys - Keep-Alive & Connection Limits.
Reuse outbound Agent with keepAlive: true: One TCP connection per downstream call wastes FDs and adds latency.
Cap maxSockets on outbound agents: Prevent a slow dependency from exhausting file descriptors.
Set per-request timeouts on outbound HTTP: req.setTimeout() or library timeout option on every call.
Limit request body size: 1-10 MB for JSON APIs; reject early with 413 - Middleware Pattern.
Always call res.end() exactly once: Hung responses are the most common raw HTTP bug.
Return proper status codes: 201 for creates, 204 for deletes, 422 for validation - not everything is 200 or 500.
Set Content-Type with charset: application/json; charset=utf-8 for international text.
Enable trust proxy with hop count, not true: Spoofed X-Forwarded-For bypasses rate limits - Reverse Proxy Awareness.
Register rate limiters after trust proxy: Otherwise every client appears as the load balancer IP.
Log client IP from req.ip, not socket address: Audit and compliance require the real client behind the proxy.
Terminate TLS at nginx/ALB/Cloudflare: Node speaks HTTP/1.1 internally unless gRPC needs h2 - HTTP/2 & HTTP/3 Considerations.
Disable proxy_buffering for SSE streams: nginx buffers break real-time delivery.
Normalize trailing slashes: Pick /users or /users/ and redirect the other.
Handle OPTIONS for CORS explicitly: Browsers preflight before POST with custom headers.
Graceful shutdown on SIGTERM: server.close() before process.exit - Kubernetes sends SIGTERM on pod delete.
Health check without auth middleware: /health or separate port for probes.
Stream large files, don't buffer: createReadStream().pipe(res) for PDFs and exports.
Use frameworks before 15 endpoints: Plain routing is fine for sidecars; public APIs need middleware conventions - Routing Without Frameworks.
Wrap async handlers in try/catch (raw HTTP): Express 5 does this automatically; raw createServer does not.
Raise ulimit -n in production: Default 1024 file descriptors is not enough under load.
Separate admin/internal ports: Metrics and health on :8081, public API on :3000.
Document proxy header contract in ADR: Which headers nginx sets, hop count, and timeout values - ops needs this during incidents.
requestTimeout on the server. It prevents one slow handler from holding connections until the load balancer gives up.
Only for local dev or gRPC. Production APIs should terminate TLS at the proxy and run HTTP/1.1 to Node.
Run rolling deploys under load (k6 or artillery) and watch for 502 spikes. If they correlate with deploys, lower keepAliveTimeout.
On private VPC east-west traffic behind mTLS mesh, yes. Never on public internet-facing endpoints.
Health sidecars, webhook receivers under 5 endpoints, and internal tools with no auth. Everything else benefits from Express or Fastify conventions.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 16, 2026