http

HTTP client tuned for production: timeouts, transport, and connection reuse

The default http.Client is deceptively easy to misuse. I always set a request timeout (either via client.Timeout for simple cases or context.WithTimeout per request) and I tune the Transport so we reuse connections aggressively without leaking idle so

Safe multipart uploads using temp files (bounded memory)

Multipart uploads are a common DOS vector if you let them allocate unbounded memory. I cap the request with http.MaxBytesReader, keep ParseMultipartForm bounded, and copy the file stream into a temp file using io.Copy. This avoids holding the whole fi

mTLS client configuration with custom root CA pool

For internal service-to-service calls, mutual TLS is a pragmatic way to get strong identity without bespoke auth headers. The main pitfalls are certificate rotation and trust configuration. I build a x509.CertPool from a dedicated internal CA, load a

Response compression (only when it helps)

Compression can dramatically reduce payload sizes for JSON and HTML, but it also costs CPU. I enable it with sane defaults and avoid compressing already-compressed content (like images). Compression can also hurt streaming responses and SSE, so I disa

Fetch API for HTTP requests and AJAX communication

The Fetch API provides modern interface for HTTP requests returning promises. I use fetch(url) to make GET requests that resolve with Response objects. The response.json() parses JSON data asynchronously. POST requests need method, headers, and body o

Streaming JSON decoding with DisallowUnknownFields

Large request bodies are where naive code falls over. Instead of io.ReadAll, I decode JSON incrementally with json.Decoder and enable DisallowUnknownFields so unexpected fields fail fast. That becomes a surprisingly strong safety net when you evolve A

HTTPtrace to debug DNS/connect/TLS timing in production-like runs

When an outbound call is slow, it’s not always the server—it can be DNS, connection reuse, or TLS handshakes. net/http/httptrace lets you instrument a single request and see exactly where time is going. I attach a trace to the request context and reco

Token bucket rate limiter for outbound calls

Outbound rate limiting is one of those “quiet reliability” features: customers rarely notice it until it’s missing. I prefer a simple token bucket using golang.org/x/time/rate because it’s well-tested and easy to reason about. Each call waits for a to

HTTP keep-alive agent for outbound calls

Creating a new TCP/TLS connection for every request is slow and it adds load on both sides. For high-traffic services calling a small set of upstreams, keep-alive reduces latency and CPU dramatically. I configure an https.Agent with sensible maxSocket

Table-driven tests for HTTP handlers with httptest

Go’s table-driven tests are one of the best “boring but effective” practices. For handlers, I use httptest.NewRecorder and httptest.NewRequest so tests run fast without networking. Each case specifies method, path, body, expected status, and sometimes

Retry on 429 with Retry-After parsing

Rate limits are normal in production; what matters is how clients behave when they hit them. Instead of hammering an upstream with immediate retries, I parse Retry-After (seconds) and sleep before retrying. I still keep an upper bound so one request d

JWT verification with cached JWKS (handles key rotation)

JWT auth is easy to get subtly wrong, especially around key rotation. Instead of hard-coding public keys, I fetch JWKS and cache it with a refresh interval so new signing keys are accepted quickly. I still validate iss and aud so tokens from other env