net/http Best Practices
Framework-free HTTP services that scale and shut down cleanly.
Search across all documentation pages
Framework-free HTTP services that scale and shut down cleanly.
Apply these rules in code review, service templates, and load tests so every stdlib HTTP surface behaves predictably under production traffic.
http.NewServeMux() in main, not http.DefaultServeMux. Prevents import-side route collisions across packages.ReadHeaderTimeout on every production http.Server. Blocks slowloris-style header stalls before handlers run.IdleTimeout to reclaim keep-alive connections. Stops descriptor leaks on quiet clients.http.MaxBytesReader. Rejects oversized payloads before JSON decode work.server.Shutdown(ctx). Rolling deploys drain in-flight work instead of force-killing.terminationGracePeriodSeconds./health without auth middleware. Load balancers need a fast liveness signal.WriteTimeout. SSE and long polls need explicit timeout exceptions.r.Context() as the first argument to all blocking downstream calls. Honors client disconnect and server deadlines.context.Canceled when the response cannot flush. Avoids misleading 500s after the client left.Content-Type before writing JSON bodies. Prevents ambiguous responses and broken clients.WriteHeader once; use http.Error for failures. First Write locks status to 200 if headers were not sent.r.Body on every path. Required for keep-alive connection reuse on the server side.ResponseWriter to capture status for metrics and access logs. Default writer hides final status from middleware.http.Client per upstream dependency. Isolates timeouts and transport tuning per SLA.http.Get from handlers. http.DefaultClient has no timeout and is process-global.defer resp.Body.Close() after client.Do. Leaked bodies exhaust connection pools.io.Copy(io.Discard, resp.Body) on non-2xx paths.MaxIdleConnsPerHost for hot upstreams. Reduces TLS and TCP churn under steady QPS.Transport on httputil.ReverseProxy. Default transport is shared and untuned for gateway paths.ErrorHandler on reverse proxies. Maps dial and header timeouts to 502/504 consistently.tls.Config.MinVersion to TLS 1.2 or higher. Documents acceptable cipher posture for external listeners.X-Forwarded-* only from known load balancers. Prevents client-spoofed IP and scheme metadata.Dedicated mux, ReadHeaderTimeout, graceful shutdown, context propagation, and client body closing are CI-worthy blockers.
Document exceptions in the service README.
Yes.
Frameworks wrap net/http; timeouts, shutdown, and client hygiene remain application responsibilities.
The checklist is deploy-gate verification.
This list is ongoing team policy for templates and review.
Running ListenAndServe with nil handler on DefaultServeMux and no timeouts.
Fix with explicit http.Server and owned mux.
Only if SLA and timeout needs match.
Different upstreams should get different clients and transports.
Wrap with http.MaxBytesReader per handler and return 413 on overflow.
Log limit breaches at warn level.
Generate or accept X-Request-ID in outer middleware.
Propagate to logs and outbound RoundTripper wrappers.
They cover stdlib HTTP fundamentals.
Add WAF, rate limiting, and authz at ingress or mesh layers as threat models require.
Stack versions: This page was written for Go 1.26.x (Green Tea GC default, go fix modernizers - verify patch at build), chi (latest - verify at build), gin (latest - verify at build), echo (latest - verify at build), google.golang.org/grpc (latest - verify at build), sigs.k8s.io/controller-runtime (latest - verify at build), kubebuilder (latest - verify at build), tinygo (latest - verify board targets at build), wazero (latest - verify at build), and golangci-lint (latest - verify linter set at build).
Reviewed by Chris St. John·Last updated Jul 18, 2026