C.W.K.
Stream
Lesson 04 of 07 · published

HTTP Versions — Same Semantics, Different Wire

~11 min · foundations, http1, http2, http3, quic, multiplexing

Level 0HTTP Newbie
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"There are three HTTPs in active use today. They all speak the same vocabulary. They just package the envelope differently."

The Family Tree

HTTP has evolved without breaking its application-layer contract. The methods, status codes, headers, and URIs you learn in this quest work identically across every version. What changes is the wire encoding: how those four parts of the message are laid down on the network.

  • HTTP/0.9 (1991) — one-line GET only. Historical curiosity; long dead.
  • HTTP/1.0 (1996) — introduced status codes, headers, methods beyond GET. Still no Keep-Alive, so a new TCP connection per request.
  • HTTP/1.1 (1997, refined through 2022's RFC 9112) — persistent connections by default, chunked transfer encoding, mandatory Host header, pipelining (rarely used). Text-based on the wire — what you'd type into netcat.
  • HTTP/2 (2015, RFC 9113) — binary framing, multiplexing (many concurrent streams over one TCP connection), header compression (HPACK), prioritization. Same semantics, much faster delivery.
  • HTTP/3 (2022, RFC 9114) — runs over QUIC (UDP-based) instead of TCP. Faster handshake (TLS bundled in), no head-of-line blocking, connection migration. Same semantics again.

What Each Version Solved

HTTP/1.1's killer feature was Keep-Alive: keep the TCP connection open so the next request reuses it. Combined with the mandatory Host header, this enabled virtual hosting (multiple sites on one IP) and made web pages dramatically faster.

HTTP/2's killer feature was multiplexing: many requests interleaved as frames on one TCP connection. Before HTTP/2, browsers opened ~6 parallel TCP connections per origin to fetch assets in parallel. With HTTP/2, one connection handles everything. Header compression (HPACK) cut repeat-request overhead — every request to the same origin used to re-send all those User-Agent, Cookie, Accept-* headers as raw text; HTTP/2 deduplicates them.

HTTP/3's killer feature was killing TCP's head-of-line blocking. In HTTP/2, multiplexed streams share one TCP connection, so a single lost packet stalls every stream. QUIC (HTTP/3's transport) handles loss per-stream, so other streams keep flowing. Also: faster handshake and connection migration (your phone can switch networks mid-request).

Semantics stay constant; only the wire evolves. If you know HTTP/1.1 methods, headers, and status codes, you know HTTP/2 and HTTP/3. The whole rest of this quest is wire-version-independent — it's about the meaning of the message, not the bytes underneath.

When You Need to Care About the Version

For most application code, the answer is never. Your HTTP client (httpx, fetch, axios, curl) and the server negotiate the version transparently. You write the same code for HTTP/1.1 and HTTP/3.

You start caring when:

  • Mobile networks — HTTP/3 wins on lossy connections (HTTP/2's head-of-line blocking hurts there).
  • Many small assets — HTTP/2/3's multiplexing beats HTTP/1.1's per-origin connection cap.
  • Long-lived streaming — HTTP/2 frames let you mix SSE streams with regular requests on one connection.
  • Firewalls / corporate networks — UDP (and therefore HTTP/3) is sometimes blocked; you need HTTP/2 fallback.
  • Debugging — HTTP/2's binary wire is not human-readable. curl --http1.1 gives you a text trace; HTTP/2 needs Wireshark or browser DevTools to decode.

cwkPippa's Version Reality

cwkPippa's FastAPI/Uvicorn backend speaks HTTP/1.1 by default. That's fine — it serves the React frontend over LAN and Tailscale, where the multiplexing wins are tiny. The public-facing cwk-site (Next.js on Vercel) gets HTTP/2 and HTTP/3 from the edge automatically; Cloudflare and Vercel negotiate the best version with each visitor's browser. I never had to write HTTP/2 code; I just let the platform pick.

Code

Force a specific HTTP version with curl and watch the response line·bash
# Watch curl negotiate HTTP versions with a server
# Use -v to see the protocol that was chosen
curl -v --http1.1 https://creativeworksofknowledge.com/ 2>&1 | grep -E '^(>|<) ' | head
curl -v --http2     https://creativeworksofknowledge.com/ 2>&1 | grep 'HTTP/'
curl -v --http3-only https://creativeworksofknowledge.com/ 2>&1 | grep 'HTTP/'

# Look for the response line:
# < HTTP/1.1 200 OK
# < HTTP/2 200
# < HTTP/3 200
# That tells you which version the server agreed to.
Python httpx — opt-in HTTP/2·python
import httpx

# HTTP/1.1 by default — works everywhere
resp = httpx.get('https://creativeworksofknowledge.com/')
print(resp.http_version)  # 'HTTP/1.1'

# Opt in to HTTP/2 (requires `pip install httpx[http2]`)
with httpx.Client(http2=True) as client:
    resp = client.get('https://creativeworksofknowledge.com/')
    print(resp.http_version)  # 'HTTP/2' if the server supports it

# httpx doesn't ship HTTP/3 yet (as of 2026); use curl --http3 for now
Compare HTTP/1.1 vs HTTP/2 for many parallel small requests·bash
# Multiplexing demo: fetch 6 assets from the same origin
# HTTP/1.1: browser opens up to 6 connections in parallel
# HTTP/2:    one connection, 6 multiplexed streams
# HTTP/3:    same as HTTP/2 but over QUIC

# Time them yourself (rough demo)
time curl -s --http1.1 -o /dev/null \
  https://creativeworksofknowledge.com/a.png \
  https://creativeworksofknowledge.com/b.png \
  https://creativeworksofknowledge.com/c.png \
  https://creativeworksofknowledge.com/d.png \
  https://creativeworksofknowledge.com/e.png \
  https://creativeworksofknowledge.com/f.png

time curl -s --http2 -o /dev/null \
  https://creativeworksofknowledge.com/a.png \
  https://creativeworksofknowledge.com/b.png \
  https://creativeworksofknowledge.com/c.png \
  https://creativeworksofknowledge.com/d.png \
  https://creativeworksofknowledge.com/e.png \
  https://creativeworksofknowledge.com/f.png

External links

Exercise

Pick a public site that supports HTTP/2 or HTTP/3 (try cloudflare.com, google.com, or any modern site). Run curl -v --http1.1 https://<site>/ and curl -v --http2 https://<site>/. Compare three things: (1) how many TCP connections were opened (look for 'Trying' lines), (2) the negotiated protocol on the response line, and (3) header bytes — HTTP/2's HPACK compression should make the request line shorter than HTTP/1.1's raw text. Bonus: try --http3-only and see if the server supports it.
Hint
curl's verbose output prefixes are: * = curl commentary (TCP, TLS), > = bytes you sent, < = bytes you received. The 'HTTP/X' on the response line tells you what was negotiated. Most CDNs (Cloudflare, Akamai, Fastly) speak HTTP/3; many origin servers don't.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.