# HTTP/2 vs HTTP/3: TCP, QUIC and Fallback

> Compare HTTP/2 over TCP with HTTP/3 over QUIC: packet loss, stream blocking, connection migration, 0-RTT replay, discovery and curl fallback checks.

Source: https://howhttpworks.com/compare/http2-vs-http3
Last reviewed: 2026-10-05

> **TL;DR:** HTTP/2 runs over TCP, so one lost packet stalls every request on the connection. HTTP/3 runs over QUIC on UDP, where a loss only stalls the streams it hit, and a connection can survive a switch from Wi-Fi to cellular. Serve HTTP/3 with HTTP/2 as the TCP fallback, and check what clients actually negotiate: an `Alt-Svc` header or a working `curl --http3` doesn't mean HTTP/3 was used.

## The Core Difference

Both protocols multiplex many requests over one connection, and both carry the same methods, status codes and headers. The difference is underneath. For HTTPS, HTTP/2 runs over TLS over TCP. HTTP/3 runs over QUIC on UDP, and QUIC has TLS built in. The [HTTP/3 setup guide](https://howhttpworks.com/guides/http3-and-quic) covers server configuration; this page is about what changes when packets get lost or the client's network changes.

| Behavior | HTTP/2 over HTTPS | HTTP/3 |
| --- | --- | --- |
| Transport | One ordered TCP byte stream | Independently ordered QUIC streams over UDP |
| Encryption | TLS 1.2 or higher | QUIC v1 uses TLS 1.3 or higher |
| Missing transport data | Can stall delivery for every HTTP stream on the connection | Missing bytes block their stream, not unrelated stream delivery |
| Header compression | HPACK | QPACK; decoding can wait for dynamic table entries |
| Client address change | TCP does not provide QUIC-style path migration | Connection IDs allow migration subject to protocol constraints |
| Negotiated ALPN | `h2` | `h3` |
| Network requirement | Reachable TCP listener | Reachable UDP listener; keep a TCP fallback |

The transport and TLS rules come from [RFC 9113 sections 3 and 9.2](https://www.rfc-editor.org/rfc/rfc9113#section-3) and [RFC 9114 sections 1–3](https://www.rfc-editor.org/rfc/rfc9114#section-1). The table assumes HTTPS; HTTP/2 also has a cleartext mode over plain TCP.

## Packet Loss: Which Request Has To Wait

Say the browser requests `/app.js` and `/account` at the same time. In HTTP/2, frames from both streams go into one TCP byte stream. If a packet carrying part of `/app.js` is lost, TCP holds back everything after the gap, including `/account` bytes that arrived fine, until the retransmission lands. HTTP/2 multiplexing can't fix this ([RFC 9114 section 1.1](https://www.rfc-editor.org/rfc/rfc9114#section-1.1)).

QUIC tracks ordering per stream, so it can hand `/account` to the application while `/app.js` waits for its retransmission. One lost packet may carry data from several streams, and those streams all wait. Each stream is still delivered in order, and congestion control still applies to the whole connection ([RFC 9000 sections 2 and 13](https://www.rfc-editor.org/rfc/rfc9000#section-2)).

So HTTP/3 has less blocking, not none. Header decoding can wait for QPACK dynamic table updates, and [RFC 9204 section 2.1.2](https://www.rfc-editor.org/rfc/rfc9204#section-2.1.2) defines these blocked streams explicitly. What HTTP/3 removes is TCP's connection-wide delivery dependency.

## Connection Migration: Wi-Fi To Cellular

A TCP connection is identified by its addresses and ports, so when your phone moves from Wi-Fi to cellular, the connection dies and the client reconnects. QUIC identifies connections with connection IDs instead. The client can move to the new network path, validate it, and keep the same connection. The same mechanism handles NAT rebinding, where a middlebox changes the client's port mid-connection.

Not every handoff survives. A server can turn off active migration with the `disable_active_migration` transport parameter, migration isn't allowed until the handshake is confirmed, and the new path has to actually work. See [RFC 9000 section 9](https://www.rfc-editor.org/rfc/rfc9000#section-9).

## 0-RTT: A Resumption Feature With Replay Risk

When a client resumes an earlier connection, QUIC lets it send application data in its very first flight ([RFC 9001 section 4.6](https://www.rfc-editor.org/rfc/rfc9001#section-4.6)). This isn't unique to HTTP/3: TLS 1.3 offers the same early data over TCP. The catch is the same in both cases. An attacker who captures early data can replay it ([RFC 8446 section 2.3](https://www.rfc-editor.org/rfc/rfc8446#section-2.3)).

A sensible policy: accept replay-tolerant reads such as `GET /logo.svg` in early data, and answer an early `POST /transfer` with [425 Too Early](https://howhttpworks.com/status-codes/425). The client then retries without early data. If a gateway forwards a request before its own client handshake has finished, it adds `Early-Data: 1`, and the origin answers 425 if it can't process that request safely. Waiting at the origin doesn't help; the early data already crossed the earlier hop. These rules come from [RFC 8470 sections 5–6](https://www.rfc-editor.org/rfc/rfc8470#section-5).

## Discovery And UDP Fallback

Servers usually advertise HTTP/3 with a response header. This one (an example) points to the same hostname on UDP port 443:

```http
Alt-Svc: h3=":443"; ma=86400
```

`ma=86400` is how long, in seconds, the client may remember the advertisement; it isn't an HTTP/3 timeout. A client typically sees this on a TCP response and tries QUIC on a later connection ([RFC 7838 section 3](https://www.rfc-editor.org/rfc/rfc7838#section-3)). To get in earlier, publish an HTTPS DNS record; clients that look it up learn about `h3` before their first HTTP response ([RFC 9460 section 10](https://www.rfc-editor.org/rfc/rfc9460#section-10)):

```text
example.com. 300 IN HTTPS 1 . alpn="h3,h2"
```

Both are hints; neither forces HTTP/3. When UDP is blocked, [RFC 9114 section 3.1](https://www.rfc-editor.org/rfc/rfc9114#section-3.1) says clients should fall back to HTTP over TCP. So keep your TCP listener up. How quickly a client gives up on QUIC is up to the client.

## When Each Fits

**Keep HTTP/2:** always, as the TCP path. It's all you need when your clients or their networks can't use QUIC, or when your endpoint only has a TCP listener today. Adding `Alt-Svc` won't open a UDP port your firewall blocks.

**Add HTTP/3 alongside it:** when your traffic has many concurrent downloads over lossy links, or clients that switch networks mid-session, such as mobile apps. That's what QUIC was designed for, but the gain varies by network. Measure real request timings on your own paths before declaring a winner.

## The Common Mistake: Testing The Advertisement

Seeing `h3` in `Alt-Svc` tells you the server sent the header. Whether UDP actually gets through is a separate question. Likewise, `curl --http3` falls back to TCP when QUIC fails, so a successful response may have come over HTTP/2 or HTTP/1.1. [curl documents both modes](https://curl.se/docs/http3.html#http3).

## Check On The Wire

You need a curl build with HTTP/3 support; look for `HTTP3` in the features line of `curl -V`. The `%{http_version}` write-out variable prints the version the request actually used ([curl manual](https://curl.se/docs/manpage.html)):

```bash
curl -V | grep -E 'Features:.*HTTP3'
curl -sS -D - -o /dev/null https://example.com/ | grep -i '^alt-svc:'
dig +short HTTPS example.com
curl --http2 -sS -o /dev/null -w '%{http_version}\n' https://example.com/
curl --http3 -sS -o /dev/null -w '%{http_version}\n' https://example.com/
curl --http3-only -sS -o /dev/null -w '%{http_version}\n' https://example.com/
```

Swap in your own endpoint. `--http3-only` turns off the TCP fallback, so it's the real test. If it fails while `--http3` succeeds, look at the version `--http3` printed and check UDP reachability; QUIC isn't working yet. `--http2` negotiates too, so read its printed version as well.

In Chrome DevTools, turn on the Network panel's **Protocol** column. It shows `h2` or `h3` for each request ([Chrome documentation](https://developer.chrome.com/docs/devtools/network/reference#columns)). Look at individual requests: one page load often mixes protocols, especially the first visit before the browser has seen `Alt-Svc`.

## Related

- [HTTP/3 and QUIC setup](https://howhttpworks.com/guides/http3-and-quic) for listeners and firewall configuration.
- [HTTP/1.1 vs HTTP/2](https://howhttpworks.com/compare/http1-vs-http2) for multiplexing over TCP.
- [Alt-Svc](https://howhttpworks.com/headers/alt-svc) for alternative endpoint advertisements.
- [425 Too Early](https://howhttpworks.com/status-codes/425) for handling replay-sensitive early requests.
