HTTP/3 and QUIC: Same HTTP, Different Transport
- date
- category
- Networking
- also in
- Frontend
- reading
- 2 min / 340 words
HTTP/2 solved an important problem:
many HTTP requests
-> many independent HTTP streams
But the streams still shared one TCP connection.
HTTP/3 changes the layer below HTTP.
HTTP/2
|
v
TCP
|
v
TLS
HTTP/3
|
v
QUIC
|
v
UDP
QUIC integrates the transport and cryptographic mechanisms needed for secure communication.
HTTP semantics stay
The frontend still writes:
fetch("/api/users");
The backend still semantically sees:
GET /api/users
All of these still exist:
status codes
headers
cache semantics
content
HTTP/3 is not a new API model.
It is a new mapping of HTTP semantics onto a transport.
Streams are more independent
QUIC has many logical streams at the transport level.
If data on one stream is lost, retransmitting that stream does not have to block delivery of data on other streams.
Simplified:
HTTP/2 over TCP
loss
|
v
shared TCP byte stream waits
|
v
multiple HTTP streams can stall
versus:
HTTP/3 over QUIC
loss in stream A
|
v
stream A waits for recovery
stream B can continue
That removes transport-level head-of-line blocking between independent streams.
It does not remove application latency
If the frontend does:
const user = await fetch("/user");
const orders = await fetch(`/users/${user.id}/orders`);
we have a dependency:
request A
|
v
response A
|
v
request B
HTTP/3 will not fix that.
A network protocol does not remove serialization created by the application.
Which matters for performance debugging.
A better transport does not replace a correct dependency graph.
Connection establishment
QUIC can reduce the cost of establishing secure communication compared with separate TCP and TLS stages.
For returning connections 0-RTT is also possible under certain conditions.
But 0-RTT has security and replay limitations, so it should not be thought of as:
every HTTP/3 request starts instantly
It is an optimization of one specific stage of connection setup.
Connection migration
A TCP connection is tightly bound to the classic tuple of addresses and ports.
QUIC uses connection IDs, which makes it easier to keep a logical session alive when the network path changes.
A practical case:
phone on Wi-Fi
|
v
switch to cellular
This matters especially for mobile devices.
The frontend does not steer it directly, but it feels the effect as a more resilient transport layer.
Does the frontend have to detect HTTP/3?
Usually not.
Application code should rely on HTTP semantics:
method
status
headers
body
The browser and the infrastructure negotiate the transport protocol.
DevTools can show the protocol in use, which helps with performance analysis.
But business logic should not look like this:
if (httpVersion === 3) {
...
}
That is exactly the strength of separating the layers.
Now we can put them all together and see what really happens after a single fetch().