---
title: "HTTP / HTTP/2 / HTTP/3"
description: "Request-response, multiplexing, QUIC"
source: https://vizlearn.app/cs/networks/http-https
---

# HTTP / HTTP/2 / HTTP/3

HTTP / HTTP/2 / HTTP/3 is how a browser fetches a page's files over one connection, and what happens when those files have to share it. HTTP/1.1 queues them. HTTP/2 flies them together on one TCP byte stream, so one lost packet freezes every stream. HTTP/3 flies them together too, but a loss stays on its stream.

Watch the same four-file page three times. First the extras form a staircase: one GET occupies the socket, the others wait. Then they overlap on one TCP connection, and a hole in that byte stream parks JavaScript and the image even after those packets have arrived. Then the same drop happens on QUIC: JavaScript and the image finish, CSS waits. Those three pictures are HTTP-layer head-of-line blocking (HOL), TCP HOL, and per-stream delivery.

## How HTTP versions share a connection

Start with one origin and one connection. That is the honest comparison. Browsers paper over HTTP/1.1 by opening about six sockets to the same host; we will come back to why that is a workaround, not a design.

In this non-pipelined HTTP/1.1 browser model, keep-alive reuses the socket, but `in_flight` is at most one. HTTP/1.1 also defines pipelining, with ordered responses; this run does not use it. `GET /style.css` occupies the connection until its `200` finishes. `/app.js` has not been sent. A loss does not freeze other in-flight streams, because there are no other in-flight streams. It just delays the queue.

HTTP/2 opens one TCP connection, one TLS session (ALPN `h2`), then many streams. Client-initiated stream ids are odd: `/` is stream 1, then 3, 5, 7. Frames from every stream share one TCP sequence. The waterfall bars overlap. That is multiplex. It is also the trap. TCP delivers a byte stream. A gap in that stream blocks delivery to the application even if later bytes have already arrived. A lost packet that carried CSS freezes JavaScript and the image too. They may be sitting in the receive buffer. HTTP/2 cannot read them until the hole is filled.

HTTP/3 runs over QUIC, which runs over UDP and carries TLS 1.3 inside the transport. The fresh-connection setup modeled here takes one round trip instead of separate TCP and TLS round trips. Streams multiplex, but delivery ordering is per stream. Loss detection and congestion control still belong to the connection. QUIC packet numbers describe transmission order on the connection, in Initial, Handshake, and Application Data packet number spaces — they are not per stream. Each stream is delivered by STREAM id plus offset, so a missing piece of CSS does not hold JavaScript. Lose the packet that carried only stream 4, and streams 8 and 12 finish. That split is the reason HTTP/3 exists. It is not "HTTP/2 but faster." On a clean path the transfer looks the same; the setup is cheaper, and a loss stays on its stream.

You do not pick HTTP/3 in frontend code. The origin or CDN advertises it with Alt-Svc or HTTPS DNS records, and the browser offers `h3` in the next handshake.

In Chrome DevTools → Network, turn on the Protocol column: `h3`, `h2`, or `http/1.1`. Timing / waterfall is the same picture as this canvas: a staircase, overlapping bars, or a stall that does or does not take the neighbors with it. Loss isolation is why HTTP/3 is worth enabling on mobile and other lossy paths. On a clean LAN it mostly saves a setup RTT.

## HTTP / HTTP/2 / HTTP/3 Step by Step

The default run is **Compare** on a four-file page: `/`, `/style.css`, `/app.js`, `/photo.jpg`. One connection. Setup plays once. Then three short chapters: HTTP/1.1 clean, HTTP/2 with the CSS packet lost, HTTP/3 with the same drop.

1. **Init.** Four files are listed. Only `/` is pending. Status names the three pictures: queue, freeze, isolate.
2. **TCP.** One round trip. Grouped: the three-way handshake is the TCP Handshake lesson. The connection row grows a TCP block.
3. **TLS 1.3.** Another round trip. Grouped: the TLS Handshake lesson. HTTP has not spoken. Setup already cost two columns.
4. **GET /.** The request goes out and a `200` comes back. `rtt = 3`, `done = 1/4`.
5. **Parse.** The HTML names the other three files. They appear as pending. They were not on the wire a beat ago.
6. **GET /style.css.** The connection is busy. JS and the image wait.
7. **GET /app.js.** Same rule. CSS is done. The image still waits.
8. **GET /photo.jpg.** Last file. The waterfall is a staircase. No two file bars overlap.
9. **HTTP/2.** Same page. The extras will fly together, and we will drop a transport packet that holds only CSS stream data.
10. **Open streams 3, 5, 7.** Three HEADERS leave on one TCP connection.
11. **DATA.** css, js, and img share one column. That is multiplex.
12. **Loss.** The CSS packet is gone. JS and the image have already arrived. They sit buffered.
13. **TCP HOL.** All three extras freeze. Later TCP bytes wait on the hole.
14. **HTTP/3.** Same drop, now on QUIC streams.
15. **DATA again.** Three frames leave on QUIC streams 4, 8, 12.
16. **Loss.** CSS is gone. JS and the image deliver.
17. **Isolation.** JS and the image are done. CSS is not. CSS still retransmits on its own; the others did not wait.
18. **Done.** Queue, then freeze, then isolate. HTTP/3 is not a faster HTTP/2; it stops one hole from blocking every stream. The origin or CDN enables it; Chrome DevTools → Network → Protocol reads `h3`.

Single-version tabs replay one machine. **Clean** on HTTP/2 finishes at `rtt = 4`; on HTTP/3 at `rtt = 3`. **Lost packet** on a single version also retries CSS: HTTP/1.1 ends at `rtt = 8` with JS still queued after the drop; HTTP/2 retransmits the missing TCP data, then unblocks all streams (`rtt = 6`); HTTP/3 retries only CSS (`rtt = 5`).

On the canvas the pipe is this RTT's frames. The waterfall is every RTT stacked. Chapter ticks mark H1 / H2 / H3. Watch whether the extra bars overlap, and whether a red gap in CSS takes the others with it.

## Why one lost packet taxes three files

HTTP/2 did not invent concurrency. Browsers already faked it with six HTTP/1.1 sockets. What HTTP/2 bought is one congestion window, one TLS handshake, and no need for a free HTTP/1.1 socket for each new concurrent request; stream and flow-control limits still apply. The seventh stylesheet no longer waits for a free socket.

What it did not buy is a loss model. TCP's contract is a single ordered byte stream. Multiplexing streams onto that stream means their fate is coupled. QUIC keeps the multiplex picture and cuts the coupling. If you remember one distinction, remember that one.

The three clean RTT counts on this toy page are 6, 4, and 3. They are not a benchmark. They are the ledger of "one connection, four files, HTML first": two setup + one HTML + three extras, or two + one + one, or one + one + one. Loss adds two columns (the drop, then the retry) except that HTTP/3 lets JS and the image finish in the first extras RTT.

## HTTP version edge cases

- **The extras do not exist until parse.** You cannot request `/style.css` before the HTML names it. A preload scanner can overlap that in a real browser; this lesson does not.
- **HTTP/1.1 HOL is a queue.** After a drop the pipe can be empty and the next GET still has not started. That is different from HTTP/2's freeze of streams that already left.
- **HTTP/2 HOL is a TCP hole.** Later frames can arrive and still be undeliverable. The accurate picture is "buffered," not "lost."
- **HTTP/3 isolation is per stream, not per connection.** A connection-level close still kills everyone. This lesson drops one transport packet that carries only CSS STREAM data. If that packet had also carried JavaScript STREAM frames, those streams would wait on the same retransmission.
- **Stream ids are not the same on HTTP/2 and HTTP/3.** HTTP/2 client-initiated streams are odd: 1, 3, 5, 7. HTTP/3 client bidirectional streams are 0, 4, 8, 12. Even HTTP/2 ids belong to the server; HTTP/3 uses the two low bits of the QUIC stream id for initiator and direction.

## Common mistakes with HTTP versions

- **Reading six HTTP/1.1 connections as "HTTP/1.1 is parallel."** This browser model sends one request at a time per socket; HTTP/1.1 pipelining is a separate option with ordered responses. HTTP/2 supports interleaved streams on one connection, subject to stream and flow-control limits.
- **Thinking HTTP/2 pipelining is the same as HTTP/1.1 pipelining.** HTTP/1.1 pipelining sent several GETs and required responses in order, which is why browsers disabled it. HTTP/2 responses can be interleaved. Loss still couples them, for a different reason.
- **Claiming HTTP/3 is always faster.** In this fresh-connection model it saves a setup RTT and then looks like HTTP/2; real setup costs vary with resumption and network conditions. The unique win is loss isolation.
- **Giving each QUIC stream its own packet numbers.** Packet numbers order packets on the connection (in Initial, Handshake, and Application Data packet number spaces). STREAM id plus offset is what lets one stream move while another is missing a piece.
- **Confusing this lesson with HTTPS.** All three runs here are encrypted (TLS under 1.1 and 2, QUIC crypto under 3). The TLS Handshake lesson is where the keys come from. This lesson is how files share the channel those keys opened.
- **Treating a status code catalog as the protocol.** `200` is furniture. The load-bearing rule is who may speak on the connection, and who waits when a packet disappears.

## A Note on Simplification

This is one connection and a four-file page. Real browsers open about six HTTP/1.1 sockets to an origin, run a preload scanner, and overlap parsing with later fetches. HPACK and QPACK compress headers; HTTP/2 server push was removed from major browser implementations, though it remains in the protocol; priority support varies; 0-RTT early data can hide another round trip on HTTP/3. Loss here is one transport packet that holds only CSS stream data, not a measured RTO. Shared congestion control, connection flow control, and QPACK dependencies can still delay other HTTP/3 streams; per-stream ordering removes transport-level head-of-line blocking, not every possible dependency.

TCP and TLS are grouped beats that point at those lessons. The RTT counts are the toy ledger, not a measurement of any real origin. RFC 9113 (HTTP/2), RFC 9114 (HTTP/3), and RFC 9000 / 9002 (QUIC) are the specifications.
