Stefan Wintermeyer (@wintermeyer) is a book author, speaker and freelancer who is specialized in Ruby on Rails, Phoenix and WebPerformance. He is located in Germany but helps his clients all over the world. Find more about his work at wintermeyer-consulting.de and his strange hobby to improve vacation times at mehr-schulferien.de. Please do open your waterfall tab in the browser to analyze those pages.
Every day I read a tweet or a posting where a web performance consulting company talks about hero images, JavaScript and how evil they are and that they have to become smaller. I agree with all those claims but actually the real problem often is not the hero image per se and many of them can actually be bigger or in a higher quality without having an impact on web performance at all.
The Waterfall Problem
We all look at the network waterfall tab in our browsers or on https://www.webpagetest.org as it was the only truth. It definitely is a super important if not the most important tool in our arsenal but it lacks one critical information: It doesn’t tell you anything about the underlying TCP-Stack and that is an important part to optimize the page load time.
The Bottleneck
For all who haven’t read High Performance Browser Networking by Ilya Grigorik yet here is a little introduction/reminder into the main problem: HTTP 1 and 2 work over TCP and TCP doesn’t know how big a bandwidth there is between the webbrowser and the webserver. To figure that out it starts with a small package of about 14 KB in the first round trip. So the server sends this 14 KB of payload (e.g. an HTML file or a fragment of an HTML file) to the browser which tells the server that it has been received in perfect condition. So the server doubles to 28 KB for the second roundtrip. The payload gets doubled until a problem occurs. Than the protocol goes one step back. This Slow-Start procedure is hard coded in all operating systems. You can not change it.
Here’s a table of the maximum payload for the first 10 round trips. All numbers are rounded. So you can send a tiny bit more.
Roundtrip | Payload (KB) |
---|---|
1 | 14 |
2 | 29 |
3 | 57 |
4 | 114 |
5 | 228 |
6 | 456 |
7 | 913 |
8 | 1825 |
9 | 3650 |
10 | 7300 |
Those roundtrips cost time. On a super fast fiber connection it might take just 10 ms for a round trip but on a slow G3 connection that will be easily a small 3 digit number. And those poor clients in Australia who use US servers have to wait a lot. And as I always tell my clients: Bandwidth is not that important for a normal webpage. Latency between the client and the server is.
In December 2018 the median desktop payload for a webpage according to https://httparchive.org/reports/page-weight is 1,584 KB. That would mean we need a total of 7 roundtrips to transport that data from the server to the client. But only with HTTP/2. With HTTP/1.1 it’s a bit more complicated and slower. But because we are all using HTTP/2 already I’m not going to dive into the HTTP/1.1 past here.
But let’s have a look at the potential maximum payload we can transport in those 7 roundtrips. It is actually 1,811 KB. So we could have sent 227 KB more without any negative impact. Those 227 KB can make a big difference for a hero image. Think bigger or better quality.
Use the space in the best way possible
You can not change the TCP protocol but you can change what you transfer with it.
- Let’s assume your index.html page is compressed 10 KB and your whatever.css is compressed 4 KB. Than you should either inline the CSS or tell your HTTP/2 server to push both files together.
- If your index.html file is compressed 15 KB big you should really try hard to get rid of that 1 KB because it takes twice as long to transfer a 15 KB file compared to a 14 KB file. Changing gzip to brotli compression probably would to the trick by itself.
- Let’s assume you just have one file (e.g. a 404 page) which is uncompressed less than 14 KB big. For that file is doesn’t make sense to compress it at all. Because we have the windows of 14 KB. By compressing the file we are waisting CPU resources and actually make the page load slower. Yes, this is an extreme example. I just want to raise the awareness of the problem
- While optimizing your hero image you should calculate with which round trip it gets transfered. All the payload of that roundtrip can be used without a penalty so you can often deliver a higher quality image.
- Sending stuff in the right order makes a big difference. The browser needs time to compile JavaScript or to render images/HTML/CSS. Use that time wise to send other stuff in parallel.
About that time budget
I’m a big fan of setting time budgets for web performance. But I also think that a budget for those round trips would actually make more sense. Because that is the one thing you can not control and you can not optimize. So in stead of saying “Let’s deliver within 1 second” you should think of it as “Let’s deliver within the first 4 TCP roundtrips”.
The future
HTTP/3 will use UDP and with UDP we don’t have the TCP slow start problem any more. So the future is bright but for now we have to think about TCP slow start when ever we optimize a webpage for better web performance.