Networking and API Design for System Design

HTTP/HTTPS

Almost every system you design communicates over HTTP. Understanding HTTP versions, methods, status codes, and how HTTPS actually secures the connection is foundational.


The Concept Explained

HTTP is a request-response protocol whose semantics have barely changed since the 1990s: a method, a path, headers, an optional body, and a status code back. What has changed three times is how those messages are put on the wire, and that is where the performance consequences live.

Semantics (stable)          methods, status codes, headers, caching rules
Wire format (changed 3x)    HTTP/1.1 text     HTTP/2 binary over TCP
                            HTTP/3 binary over QUIC
KEY CONCEPT

HTTP/1.1, HTTP/2, and HTTP/3 express the same semantics. A GET /orders/123 means exactly the same thing on all three. What differs is concurrency and the cost of loss, which means version choice is a performance decision, not an API design one. Your API contract does not change; how many requests can share a connection does.

Method semantics and status codes as an API contract are covered in Lesson 2.5, and the free Networking Fundamentals course walks the versions from an operational angle. This lesson focuses on what each version costs and what HTTPS actually buys.


How It Works

HTTP/1.1: one request at a time

A connection carries one outstanding request. Keepalive lets you reuse it sequentially, but request two cannot start until request one's response completes.

connection 1   [── req A ──][── req B ──][── req C ──]
                             B waits for A, C waits for B

The workaround browsers adopted was to open about six connections per origin and spread requests across them. That is a cap, not a solution, and it is the same limit that breaks SSE with multiple tabs (Lesson 5.3).

This is head-of-line blocking at the application layer: a slow response delays unrelated requests queued behind it.

HTTP/2: multiplexing over one connection

HTTP/2 makes the wire format binary and introduces streams. Many requests and responses interleave as frames over a single TCP connection.

connection 1   [A][B][C][A][C][B][A]     interleaved frames, all in flight together

What this buys:

  • Real concurrency on one connection, so the six-connection limit disappears
  • Header compression (HPACK), which matters because headers are highly repetitive across requests; a large cookie or Authorization header sent on every request compresses to almost nothing after the first
  • Server push, which sounded promising, was widely implemented, and was ultimately removed from browsers because it mostly wasted bandwidth pushing things clients already had

HTTP/2 is also the foundation of gRPC (Lesson 2.7), which uses streams directly.

The catch: TCP still delivers bytes in order. If one packet is lost, the kernel holds back everything behind it, including frames belonging to other streams that arrived perfectly well.

HTTP/1.1   head-of-line blocking at the HTTP layer   (one request blocks the next)
HTTP/2     fixed that, but TCP still blocks below it (one lost packet blocks all streams)

So HTTP/2 moved the problem down a layer rather than eliminating it. On a clean network it is a clear win; on a lossy mobile link, multiplexing everything onto one TCP connection can be worse than several connections, because one lost packet now stalls all of them.

HTTP/3: streams that are actually independent

HTTP/3 runs over QUIC (Lesson 1.3), which implements reliability per stream over UDP. Loss in one stream no longer blocks the others, because the transport itself understands stream boundaries.

The gains, in order of practical value:

  • No transport-level head-of-line blocking. The remaining reason HTTP/2 underperformed on lossy links is gone.
  • Faster connection setup. Transport and TLS handshakes are combined: one round trip for a new connection, zero for a resumed one, against two or three for TCP plus TLS.
  • Connection migration. The connection is identified by a connection ID rather than the address and port four-tuple, so a phone moving from WiFi to cellular keeps its connection alive instead of re-establishing everything.
  • Encryption is mandatory. There is no unencrypted HTTP/3.

The costs: UDP is blocked or throttled on some restrictive networks, so a TCP fallback is required; QUIC runs in userspace, so it uses measurably more CPU than kernel TCP at high volume; and packet-level inspection tooling sees less, since more of the transport is encrypted.

HTTP/1.1HTTP/2HTTP/3
TransportTCPTCPQUIC over UDP
FormatTextBinaryBinary
Concurrency~6 connectionsMultiplexed streamsMultiplexed streams
App-layer HOL blockingYesNoNo
Transport HOL blockingYesYesNo
Header compressionNoHPACKQPACK
New connection setup1 RTT + TLS1 RTT + TLS1 RTT combined
Resumed setup1 RTT + TLS1 RTT + TLS0 RTT
EncryptionOptionalEffectively requiredMandatory
Connection migrationNoNoYes

What HTTPS actually provides

TLS gives three properties, and being precise about them matters because people routinely conflate the first two:

  • Confidentiality. Intermediaries cannot read the contents.
  • Integrity. Tampering in transit is detected.
  • Authentication. The certificate proves you are talking to the host you asked for. This is the property that stops a man in the middle, and it is what a certificate warning is telling you has failed.

The handshake at a design level:

1. Client offers supported versions and cipher suites, plus a key share (TLS 1.3)
2. Server picks, returns its certificate and its key share
3. Client validates the certificate chain against its trust store
4. Both derive the same symmetric session key
5. All subsequent traffic uses fast symmetric encryption

The design-relevant points:

Asymmetric crypto is used briefly, symmetric crypto does the work. Public key operations are expensive and happen once per handshake; the session then uses symmetric encryption, which modern CPUs accelerate in hardware. This is why "TLS is slow" is outdated: the handshake is the cost, not the encryption.

TLS 1.3 halved the handshake, from two round trips to one, and supports 0-RTT resumption for repeat connections. On a 100ms link that is a 100ms saving per new connection, which is significant for short requests.

0-RTT has a caveat worth knowing. Data sent in the zeroth round trip can be replayed by an attacker, so it must only carry idempotent requests. This is Lesson 2.2 reappearing at the transport layer.

Certificate validation is a dependency. Expiry causes total, sudden outages, and revocation checking (OCSP) can add latency or fail. Certificate expiry remains one of the most common causes of self-inflicted downtime, which is why automated renewal is not optional.

Where TLS terminates

An architectural decision with wide consequences:

Terminate at the edge          client ══TLS══> LB ──plain──> services
  + Everything downstream can route, cache, inspect, and log
  + Certificates managed in one place
  - Internal traffic is unencrypted

Terminate at the edge, re-encrypt    client ══TLS══> LB ══TLS══> services
  + Inspection at the edge AND encryption internally
  - Two handshakes, more CPU, more certificates

Passthrough to the service     client ══════════TLS══════════> service
  + True end-to-end encryption
  - The LB sees only addresses and ports: no path routing, no L7 features

Most systems terminate at the edge and re-encrypt internally, or terminate at the edge with a service mesh handling internal mTLS. Passthrough is chosen when regulation or threat model demands that no intermediary can read the traffic, and it costs you every layer 7 capability at the edge, including the API gateway (Lesson 3.1).

Connection reuse is the optimization that matters

Given the setup costs above, the single most valuable HTTP performance practice is not opening new connections.

New connection    TCP handshake + TLS handshake + request   ~3 RTT
Reused connection                                 request   ~1 RTT

Over a 100ms RTT that is 300ms versus 100ms for identical work. Every HTTP client library has a connection pool; using it, sizing it, and keeping connections warm are what make a service with heavy upstream traffic fast. This is also why an API gateway must pool upstream connections (Lesson 3.1), and why gRPC's single long-lived connection is efficient but confuses L4 load balancers (Lesson 2.7).


System Design Implications

  • Version choice affects capacity, not contracts. Moving to HTTP/2 or HTTP/3 changes concurrency and latency without touching your API.
  • HTTP/2 is not strictly better than HTTP/1.1 on lossy links. Multiplexing onto one TCP connection concentrates the cost of loss. This nuance is a strong thing to raise.
  • TLS termination determines what your infrastructure can do. Gateways, WAFs, caches, and observability all need plaintext. Passthrough encryption is a deliberate trade of capability for confidentiality.
  • Certificate lifecycle is production infrastructure. Automate renewal and alert on expiry well in advance; expiry outages are total and abrupt.
  • Header size is a real cost at scale. With HTTP/1.1 a large JWT is re-sent in full on every request (Lesson 4.3). HTTP/2's compression makes repeated headers nearly free, which is a concrete argument for the upgrade on token-heavy APIs.

Tradeoffs and Decision Framework

HTTP/2 versus HTTP/1.1. HTTP/2 wins on clean networks with many parallel requests. On very lossy paths, its single connection amplifies loss.

HTTP/3 versus HTTP/2. HTTP/3 wins on mobile and lossy networks, and on connection setup. It costs more CPU and needs a TCP fallback where UDP is blocked.

Terminate versus passthrough. Terminating enables everything at layer 7. Passthrough gives end-to-end confidentiality and blinds your edge.

0-RTT resumption. Saves a full round trip, and permits replay. Enable it for idempotent requests only.

Connection pool sizing. Too small queues requests behind a busy pool; too large exhausts ephemeral ports and file descriptors on both ends (Lesson 1.3).


Common Mistakes

Assuming HTTP/2 is always faster. Transport head-of-line blocking still applies, and is worse when everything shares one connection.

Not reusing connections. Pays 2-3 extra round trips on every request.

Thinking TLS is expensive per byte. The handshake is the cost; symmetric encryption is hardware-accelerated.

Confusing encryption with authentication. Encryption without certificate validation protects you from eavesdropping and not from talking to an impostor.

Manual certificate renewal. A guaranteed future outage with a known date.

Enabling 0-RTT for non-idempotent requests. Replayable by design.

Ignoring header bloat on HTTP/1.1. Large tokens and cookies re-sent uncompressed on every single request.

Assuming passthrough TLS still allows path routing. The load balancer cannot read what it cannot decrypt.


INTERVIEW QUESTION

What are the key differences between HTTP/1.1, HTTP/2, and HTTP/3? How does each affect the performance of a system you are designing?