Network request waterfalls show what resources are loaded when opening a website. This helps developers and website owners understand why different content elements show up at different times and how it can be optimized.
Recently I made a wall calendar with 12 request waterfalls for a conference, highlighting different web performance issues. This article goes through these waterfalls one by one and explains what they show. You can also test your own website to see what you can optimize.
Interested in a paper calendar? Request one here.
Number 1: Prioritizing the LCP image
Here's the first request waterfall. If you're not familiar with this type of visualization, here's what you can see in a page speed waterfall:
- There's a list of resource URLs on the left, showing what files are downloaded
- On the right the bar indicates when the start and end time of the request
- The request bar itself can be broken down into network connection steps, requesting the resource (green), and downloading the file (blue)
- The filmstrip view lets you correlate the time when a request is made with the time that content appears on the page
- The horizontal blue line indicates the First Contentful Paint milestone and the red line indicates the Largest Contentful Paint
Now, what's happening on this page and what could be done to render the page more quickly and optimize the Largest Contentful Paint?
Websites always start loading with the HTML document. Then once that's loaded you can see the browser making other requests for resources referenced in the HTML code.
Some of these resources will be render-blocking, which means visitors can see no content until after these resources have been loaded. In this example this applies to the CSS stylesheets and we can see that the page remains blank until they have been loaded. (And then there's a small gap until the First Contentful Paint due to a CPU rendering task shown in orange.)
Improving the First Contentful Paint
We have two main options to improve the First Contentful Paint (FCP) here.
First, we could load all render-blocking resources from the same domain name. Right now, after establishing a connection to
january.com, new connections to
static.com are required to load the CSS files. Each connection takes time to establish.
Second, we could look at optimizing the size of the 100 kilobyte CSS file. In this case the CSS contains embedded base64 images that could be loaded separately when needed.
Improving the Largest Contentful Paint
Improving the FCP will also improve the Largest Contentful Paint (LCP). But there's something else going on with the LCP image.
The LCP image element uses native HTML image lazy loading. That means the browser will only start loading the image when it appears in the viewport. To know if an image is in the viewport the browser first needs to render the page. We can see that the LCP image only starts loading after the initial paint.
To load the LCP image earlier we can remove the
loading="lazy" attribute. To increase the request priority even further we can use the
fetchpriority="high" attribute on the image tag. Doing this makes sense for the main image on the page.
Number 2: Showing page content earlier
Take a look at the render-blocking resources in this waterfall. What can be optimized?
This site uses CSS @import to reference a stylesheet from within another stylesheet. This results in a sequential chain of requests.
In this case the second CSS file is also loaded from a different domain, which means it requires three round trips on the network just to create a new server connection.
The stylesheet could either be embedded directly in the first one or preloaded so that the browser can start loading it as soon as the HTML has loaded.
Number 3: Request chains
Why does this page take so long to render?
Number 4: Server connections
Normally once a server connection has been set up it is reused for future requests to the same server.
However, here we can see many server connections being created to the same server. What might be causing this?
This website uses the older HTTP/1.1 protocol. In a full test result we can confirm this by expanding the request details.
HTTP/1.1 can only make handle request at a time. To deal with that, browsers create up to 6 connections to the same server.
When that six-connection limit is reached the browser waits for other requests to finish first to be able to re-use those connections.
Making these render-blocking requests sequentially slows down rendering. Upgrading the server to HTTP/2 would allow parallel requests on the same connection and solve this issue.
Number 5: Slow main page content
What kind of website is it and why does it take a while to render?
Here we can see that there aren't many render-blocking resources and the page header appears quickly. However, then little else appears except for a spinner.
We can also see that downloading a large data file takes a long time. Compressing this file or splitting it up into separate smaller requests could help load the most important data early on and start rendering the page.
Number 6: Prioritizing different requests
What could be done to improve the Largest Contentful Paint metric on this page? The asterisk in the priority column indicates a priority change from low to high.
This page starts rendering really quickly, but downloading the LCP image is slow even though it is reasonably sized at 68 kilobytes.
async keyword is actually used to tell the browser that this script is not render-blocking. Preloads can often cause performance degradations when used in this way.
fetchpriority="high" to the
Number 7: Web fonts
Web fonts allow website owners to use fonts on their website that aren't installed on the user's computer. However, they can also cause performance problems.
Why does text only show up late in this waterfall?
This site is unusual in that images are appearing before the text. However, the LCP element is a paragraph on the page, so the LCP only happens late after the font has been loaded.
An immediate solution would be to use a fallback font until the web font has loaded. This can be achieved by adding the
font-display: swap style and a second font in the
The browser also doesn't discover the font file until after the Google Fonts stylesheet has been downloaded. A preload tag could be used to tell the browser the font is needed directly in the HTML.
Number 8: Background images
What could be done to load the background image more quickly here?
Because the LCP image is loaded using a CSS
background-image rule the browser first has to render and style the page in order to realize that the image needs to be downloaded. Due to this we can see that the request only starts after the First Contentful Paint.
Again, a preload tag with
fetchpriority="high" for the LCP image would address this issue.
Number 9: Invisible content
In this waterfall the render-blocking resources finish loading fairly quickly. But the page content becomes visible only much later. Why could this be?
This site contains an anti-flicker snippet that's commonly used by A/B testing tools. These snippets hide all body content using
opacity: 0 until A/B tests or other customizations have been loaded.
Here we can see that content only shows up after
gtm.js has finished loading and the A/B testing code has run.
To address this you could:
- disable the content hiding and accept some flicker when page content changes
- only apply the content hiding styles to specific page components like h1 or buttons that are often edited in A/B tests
Number 10: Resource competition
What's going wrong on this page?
As mentioned above, preloading fonts is often a good idea. However, if you're preloading 10 or more fonts something is clearly going wrong. This will simply divert network bandwidth away from more important resources. As a result, the HTML document and render-blocking CSS only finish loading late.
Only preload the 1-3 most important fonts for your page.
Number 11: Resource sizes
What's the main thing delaying rendering here?
The HTML document is 221 kilobytes large after gzip compression. That means it takes a while to fully download.
You can use the HTML Size Analyzer to break down your HTML download size.
For example, you may discover that it contains embedded images or fonts.
Number 12: A fast website
What makes this website so fast?
The HTML document is the only render-blocking request and content becomes visible quickly after the request has finished.
All resources are loaded from the same domain, which means only one server connection needs to be created.
Important resources like the LCP image are prioritized. Less important resources are loaded with low priority, for example by lazy loading some images and using the
defer attributes for scripts.
The site also uses 103 Early Hints to start loading fonts and key images early, even before the server starts sending the full HTML response.
Request waterfalls provide a lot of information about your website, but it can be hard to interpret them correctly. Hopefully this article was able to highlight some common patterns you can spot. You can try testing your own website here.
Interested in monitoring your page speed over time and optimizing your Core Web Vitals? Try DebugBear for free.