See It Flow
Interactive guide · 11 chapters · ~20 min read

HTTP · Production Engineering

Everything that turns a working HTTP endpoint into a production-grade service — performance, caching, edge delivery, load balancing, security, and how to debug it all when it breaks.

Mangesh MandlikLast updated:
01 · Speed

Performance

HTTP performance is dominated by round trips, not raw bandwidth — every extra hop adds latency that bandwidth cannot buy back.

A cold HTTPS request can pay for DNS resolution, a transport handshake (TCP for HTTP/1.1 or HTTP/2, QUIC for HTTP/3), a TLS handshake, and then the actual request/response. DNS may already be cached and TLS may resume, but on a cold path each extra round trip is visible latency. Connection reuse (keep-alive), HTTP/2 multiplexing, and reducing the number of distinct hostnames all attack this same problem: fewer setup round trips.

Keep-alive

Reusing one TCP connection for multiple requests instead of opening a new one each time. Default in HTTP/1.1, essential in HTTP/2/3.

Preconnect / DNS-prefetch

<link rel="preconnect"> lets the browser start DNS + TCP + TLS for a critical third-party origin before it's actually needed, hiding that latency behind other work.

02 · Payload size

HTTP Compression: gzip and Brotli

HTTP compression shrinks responses so they arrive faster — it trades CPU time for network time, almost always a good trade.

gzip

The long-standing default — supported everywhere, a large reduction for text (often well over half, depending on content), cheap to decode.

Brotli

Google's newer algorithm. Brotli can compress text assets more efficiently than gzip, but the difference depends on the content and compression settings. Precompressing static assets avoids paying the compression cost on every request.

Content-Encoding negotiation

The client advertises what it accepts via Accept-Encoding: gzip, br; the server picks one and declares it via Content-Encoding. This is HTTP content negotiation — the same mechanism used for language and format negotiation.

03 · Caching in production

Browser Caching

The caching headers you choose directly control how fast repeat visits feel and how quickly deploys propagate.

Immutable, versioned assets

Cache-Control: public, max-age=31536000, immutable on a content-hashed filename (app.a1b2c3.js). Safe because a new deploy is a new URL — the old cached copy never needs to be invalidated.

Always revalidate HTML entry points

Cache-Control: no-cache (revalidate every time) on index.html itself — it's the one file that must always reflect the latest deploy's asset references.

Stale-while-revalidate

Serves a slightly stale cached response immediately while fetching a fresh one in the background — trades perfect freshness for zero perceived latency.

04 · Edge delivery

CDN

For HTTP services, a CDN mainly changes where requests terminate and where cache hits happen: at an edge location near the user instead of at your origin.

For static assets and other cacheable responses, the CDN shortens the user-facing round trip and shields the origin from repeat traffic. For uncacheable requests, it still often terminates TLS, enforces edge policy, and forwards over long-lived origin connections. The deep mechanics of edge topology, cache keys, and purge behavior belong in the dedicated CDN fundamentals guide.

Origin pull vs push

Origin pull: the CDN fetches from your origin on a miss, then reuses that cached copy for later HTTP requests. Origin push: you preload content into the CDN ahead of demand. For typical web apps, origin pull is the common default.

Cache invalidation

For HTTP deploys, versioned asset URLs are still the cleanest answer: a new filename produces a new cache key everywhere. Purge APIs exist for emergencies and HTML/API corrections, but they are operational tools, not the primary deployment strategy.

05 · The front door

Reverse Proxy

In an HTTP stack, the reverse proxy is the L7 front door: it terminates client connections, normalizes requests, and decides how traffic should reach the app tier.

TLS termination

The proxy commonly holds the certificate and decrypts HTTPS traffic once, then forwards to application servers over an internal network using plain HTTP or re-encrypted HTTPS, depending on trust boundaries and compliance needs.

Request routing

For HTTP traffic, this is where host-, path-, and header-based routing happens — for example api.example.com to one service and /admin to another — without every backend having to expose itself directly to the internet.

Buffering & rate limiting

HTTP-aware proxies can buffer slow uploads, enforce request size limits, add or scrub forwarding headers, and apply coarse rate limits before expensive application work begins.

06 · Distributing traffic

Load Balancing

For HTTP services, load balancing is the availability layer that spreads requests across replicas and stops sending new traffic to unhealthy ones.

Round-robin

Round-robin works well when HTTP requests are similar in cost and connection reuse is not creating large imbalance.

Least-connections

Least-connections is often better when some HTTP requests stream for a long time or have widely different durations, though implementation details matter under HTTP/2 and HTTP/3 where one client connection can carry many requests.

Health checks

Health checks should probe something representative enough to catch broken instances, but cheap enough not to become load themselves. Removing a backend from rotation is what gives HTTP services graceful failure handling during deploys and incidents.

L4 vs L7

L4 load balancers see TCP/UDP metadata only and balance transport connections without parsing HTTP. L7 load balancers understand HTTP details such as host, path, and headers, which enables smarter routing at the cost of more work per request.

07 · Identity

Authentication

HTTP itself has no concept of identity — every authentication scheme is a convention layered on top of headers.

Session cookies

Server creates a session, sends an opaque id via Set-Cookie, browser resends it automatically. Simple, but requires server-side session storage (or a signed token).

Bearer token (often JWT)

The client sends Authorization: Bearer <token> on each request. That token is often a signed JWT the server can verify without consulting a session store, but revocation, rotation, and token size become important trade-offs.

OAuth 2.0 / OIDC

A delegated-authorization protocol — lets a user grant a third-party app limited access to their account on another service (e.g., "Sign in with Google") without sharing their password.

08 · Hardening

Security Headers

A handful of response headers close entire classes of browser-side vulnerabilities for free.

Content-Security-Policy

Whitelists where scripts, styles, and other resources may load from — a strong defense-in-depth layer against XSS, alongside output encoding.

Strict-Transport-Security (HSTS)

Tells the browser to never connect over plain HTTP again for this domain for the max-age period, even if the user types http:// — closes the window for SSL-stripping attacks after the first visit (preloading closes the first-visit gap too).

X-Content-Type-Options: nosniff

Stops the browser from guessing ("sniffing") a different content type than the server declared — prevents a crafted upload from being executed as script.

X-Frame-Options / frame-ancestors

Prevents the page from being embedded in an <iframe> on another site. CSP frame-ancestors is the modern control and X-Frame-Options its older equivalent; together they are the primary defense against clickjacking.

httpIllustrative response headers for an HTML entry point (values are examples to adapt, not a drop-in policy)
HTTP/2 200
content-type: text/html; charset=utf-8
cache-control: no-cache
content-encoding: br
vary: accept-encoding
strict-transport-security: max-age=31536000; includeSubDomains
content-security-policy: default-src 'self'; frame-ancestors 'none'
x-content-type-options: nosniff

The hashed JavaScript/CSS it references would instead carry cache-control: public, max-age=31536000, immutable. Vary: Accept-Encoding is required whenever the server picks the encoding per request, so shared caches do not hand a Brotli body to a client that did not ask for it.

09 · Observability

Monitoring

You can't fix what you can't see — HTTP services live or die on the quality of their observability.

The four golden signals

Latency (how long requests take), traffic (requests/sec), errors (4xx/5xx rate), saturation (how close to capacity). Almost every dashboard for an HTTP service should surface these four first.

Structured access logs

Every request logged with method, path, status, duration, and a trace id — the raw material for debugging any single incident after the fact.

Distributed tracing

A single user request often fans out across a load balancer, several services, and a database. Trace ids propagated via headers (e.g., traceparent) let you reconstruct the full path of one request across every hop.

10 · What breaks

Common Production Issues

Most HTTP incidents fall into a handful of recurring shapes — recognizing the shape is half the fix.

Connection pool exhaustion

The app server or database driver runs out of available connections under load, and every new request queues or fails — often masquerading as generic timeouts.

Thundering herd on cache expiry

A popular cache key expires and hundreds of simultaneous requests all miss at once, hammering the origin. Fixed with jittered TTLs or a request-coalescing lock ("only one request refetches, others wait").

Retry storms

Clients retrying failed requests without backoff can turn a brief blip into a sustained outage — the retries themselves become the overload. Exponential backoff with jitter is strongly recommended for any automated retry logic.

11 · Diagnosis

Debugging

The browser's Network tab and a handful of curl flags cover the overwhelming majority of HTTP debugging needs.

curl -v

Shows the full request and response headers, TLS handshake details, and timing — the fastest way to see exactly what's on the wire without a browser in the way.

Timing breakdown

Browser devtools split every request into DNS, connect, TLS, TTFB (time to first byte), and download — each phase points to a different class of problem (DNS → resolver issues, TTFB → slow backend).

bashcurl -w timing breakdown — command plus illustrative output (constructed for teaching, not measured on a real service)
$ curl -s -o /dev/null -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' https://www.example.com/
dns=0.012 connect=0.045 tls=0.079 ttfb=0.171 total=0.176

These values are cumulative from the start of the request, so subtract neighbours: DNS 12 ms; TCP connect 45 − 12 = 33 ms (about one round trip to the server); TLS 79 − 45 = 34 ms (a one-round-trip TLS 1.3 handshake); waiting for the first byte 171 − 79 = 92 ms (one round trip plus roughly 59 ms of server think time); download 176 − 171 = 5 ms. A large connect points at the network path, a large tls at handshake round trips or certificate chain size, and a large TTFB gap at the backend or an origin fetch behind a CDN.

Correlation ids

When a request fails, the single most useful thing a server can return is a request/trace id in the error response — lets you find the exact server-side log line without guessing by timestamp.

Key takeaways
  • Performance is dominated by round trips — reduce hops (keep-alive, HTTP/2, CDN edge) before optimizing bytes.
  • Version asset URLs and cache them forever; always revalidate (no-cache) the HTML entry point.
  • Load balancers need health checks with a grace period, or they overreact to blips.
  • Security headers (CSP, HSTS, nosniff, frame-ancestors) are free — enable them by default.
  • Idempotency keys prevent retries from causing double side effects.
  • P99 latency, not average, is what defines the worst user experience.