mbg / logs

Differences in HTTP versions

I wrote up some notes on using HTTP versions in production to help clarify some thoughts on transport mechanisms for larger systems. I’ve expanded a little on those for a blog post.

Disclaimers

I do not work on web browsers or HTTP implementations. These are notes from vaguely using them to make backend servers talk to each other.

There are also changes to the actual bytes being sent over the wire, their textual format, how headers work, and more. I wanted to write about the performance qualities instead of those differences.

<1.1

I have no idea. I have never seen anyone use this. I think it was the wild-west before HTTP 1.1 existed.

1.1

This is the big one! It’s what’s assumed you’re using when someone says HTTP.

I’ve only ever seen it used with TCP or a Unix domain socket stream as the data links.

Each request is a new connection setup and teardown. In TCP this is SYN-ACK-SYNACK in TCP. TLS negotiation on top of that is 4 parts, the first part usually sent with the SYNACK. This is six messages going back and forth total. That’s a lot to send before you get to any content!

Connection reuse becomes a bottleneck eventually because connections are expensive in both latency and computation to create.

2.0

HTTP 2.0 is commonly shortened to H2. I’ve only ever seen this used over TCP. People using domain sockets have tended to stick to HTTP 1.1 mostly for simplicity and less because they don’t want the features H2 offers.

Over the public internet, you are usually doing this with TLS negotiated first, then upgrading the connection to H2. This is called Application-Level Protocol Negotiation (ALPN). This is four messages back and forth. You will note that four messages is less than six messages, which is what we had to do before. Great!

In private, you may be lazier and be using H2 without TLS. It’s called H2C. Most browsers don’t support it. You probably shouldn’t use it.

A big focus for H2 was improving the connection creation work. In addition to making the handshake for connections simpler, H2 introduced the concept of independent streams within an HTTP connection. You can send requests and responses to multiple streams without blocking one on the other. This makes things go fast! Vroom!

The new latency blocker is losing packets at the TCP level instead of the HTTP level. Those requests can still be blocked by the underlying TCP packets getting lost since those are strictly ordered. If you receive packets out of order, you usually cannot use the later ones without receiving the earlier ones. So if packet 1 goes missing out of k, k-1 are wasted and you need that many more retransmissions than if the kth had been lost.

3.0

Remember those messages we need for ALPN? And how a bad TCP connection could ruin our fancy H2 streams? HTTP3/H3 wants to fix that by exising TCP entirely and moving to UDP.

There are no connections, so my connection setup time is infinitely faster!

My packets don’t have to be ordered, so if you drop one who cares! I’ll resend it with a new packet number if you don’t ack me!

These are not properties that people think about a lot, so H3 doesn’t have great adoption. There’s less of a benefit from H2->H3 with the head-of-line blocking improvements than there is from H1->H2 with connection multiplexing.