How to have your cake and h2
Tue, Aug 15, 2017This article discusses best practices for deploying h2 on a site that has already been optimized for h1 peformance. You may not find this optimization worth your time.
Deploying h2 w/o breaking http performance and older browser performance
HTTP2 (H2) is ready to be used now, it’s pretty widely supported, but there’s a problem. In order to maximize h2 performance it sort of seems like you need to de-optimize for http/1.1. Wouldn’t it be nice if you could optimize both?
The good news is you can. Most http/1.1 performance tricks matter less, but they don’t hurt h2 deployments, with one notable exception: domain sharding.
What is domain sharding?
For the uninitiated, domain sharding is an attempt to trick the browser, and it requires a bit of a backstory.
In the early days of the web, user agents were asked to only make 2 connections per server, also at that time the only way to know you were done reading a response was to wait for the server to close the connection. 1 request == 1 tcp connection.
This was fixed in later versions of http by keepalive, content-length, and chunked encoding. Essentially the protocol developed a way to indicate when a response had finished and a new request could be sent. This is a huge benefit for browsers because They can skip a lot of work associated with connection creation and teardown. The new rule is at any given time there’s only 1 request per tcp connection, but N request response pairs can be sent on a TCP connection (we’ll skip the whole pipelining thing for now since nobody really ever bothered to implement it).
Unfortunately browsers were now kind of hamstrung if they wanted to get download content ASAP. They could only have 2 downloads active at any one time and web pages outgrew this. Servers became beefier, and so did browser, but everyone who was following the rules was stuck.
Unless they cheated.
A bunch of web developers said “hey, what if I used 10 image domains that all went to the same server, then my browser could download 20 images at once right?”.
They were right.
So domain sharding became popular again, and we as a community decided that it was totes ok to have 20 connections being used when rendering a page, because that makes things fastest.
Fixing the http/1.1 domain sharding problem
Forcing your browser to do DNS lookups/TCP connections/TLS handshakes for a.example.com, b.example.com, c.example.com is a ton of work. I mean, it’s not as expensive as parsing HTML, basically nothing your computer does is as expensive as parsing HTML, but it’s still a lot of wasted latency waiting for network round trips to complete. That robs you of some of the biggest performance wins of h2, ideally all those domains would be merged into one domain, let’s call it images.example.com, but that would destroy http/1.1 performance.
Luckily, there’s a trick you can use that will let you use your old performance optimized http1/1 pages in a way that gives you almost all the benefits of a page optimized for h2. It’s called HTTP/2 connection coalescing. Daniel Stenberg from the curl project has written about it before.
In h2 with TLS (which is basically the only kind of h2 anyone cares about) you have the concept of “authority”. Essentially what this means is that if you have a cert valid for domains {www,a,b,c}.example.com AND all 4 of those DNS records are the same (same ips or same CNAMEs) then h2 connection coalescing kicks in and all your requests can get multiplexed over a single h2 connection. The only overhead introduced is the DNS lookups, but there’s no need for a TCP connect and TLS handshake for your remaining domains.
Why do the DNS records have to match? Essentially it’s to avoid confusion and possible security bugs. If I had the same cert installed on 2 totally different machines I’d be quite surprised if one machine started getting traffic for the other machine out of the blue. Checking the DNS records ensures that the server handling the traffic can handle traffic for the other domains.
Here’s an example you help illustrate. Take the following document at www.example.com which displays 20 products. In order to get around browser connection limits, they have sharded the image domain.
<html>
<head>
<script src="https://a.example.com/app.js"></script>
</head>
<body id="app">
<img src="https://b.example.com/product1.jpg"/>
....
<img src="https://c.example.com/product20.jpg"/>
<body>
</html>
A non-h2 browser might make 3 DNS lookups, 3 * (max connections per hostname) connections. Around 21 to 24. It will do that many TLS handshakes. This is a pretty heavy page, and it might be the best you can do on regular http (non-TLS) pages.
In h2-land with a properly configured cert AND DNS (like below) you can reduce that down to 3 DNS lookups and 1 TCP connection and 1 TLS handshake, which will almost certainly be much faster.
SubjectAltNames=DNS:a.example.com,DNS:b.example.com,DNS:c.example.com,DNS:example.com
dig a.example.com
...
CNAME a.example.com. example.com.
Summary
Hopefully this makes sense. I don’t imagine this is too useful for smaller sites or people who know their customers have nice fast modern browsers. But for bigger companies that want to do some extra work to make sure all their customers get the best experience possible, but don’t want to change their app code, I recommend leveraging this technique of using a wildcard cert or a cert with lots of SAN entries.
p.s. If you like web performance and you want to help 260 million people save time, you should come work with me at Walmartlabs. We do the whole remote thing.
In case those links rot, feel free to email me at mailto:shanemhansen@whitane.com if you want to talk about if this group is right for you.