A waterfall chart is the page load drawn as a timeline: one row per file, a bar from the moment the browser asked for it to the moment the last byte arrived, in the order the requests started. It is the one view that shows why a page is slow rather than that it is slow, and most of its patterns can be read in a minute once you know what to look for. This guide covers where to get a waterfall, what the parts of a bar mean, the six shapes that explain most slow pages, and the order to fix them in.
Quick answer
- Read a waterfall top to bottom and left to right. The first bar is the HTML; everything under it waited for something above it.
- A long first bar is the server. A staircase is a dependency chain. A wide gap before the big image is late discovery. Many thin bars from one host is HTTP/1.1. A short bar followed by another to the same page is a redirect.
- The vertical lines mark when the page became usable: DOMContentLoaded and Load in DevTools, plus First and Largest Contentful Paint in WebPageTest.
- Fix in this order: redirects, server response, render-blocking files, the LCP image's discovery, third parties, then connection count.
- The speed test shows a waterfall of the first 60 requests under the metrics, with the document, render-blocking files and large images coloured.
Why the waterfall matters
Metrics tell you the outcome: LCP 4.1 s. Audits tell you a probable cause with an estimate: "eliminate render-blocking resources, 1.2 s". The waterfall shows the mechanism: the HTML took 900 ms to arrive, then two stylesheets and a tag manager blocked for 700 ms, and the hero image was not even requested until a script that ran after those had built the slider. Each of those is a different fix, and only the waterfall tells them apart.
Where to get one
- Chrome DevTools → Network. Reload with the panel open. The Waterfall column is on the right; hover a bar for the timing breakdown. Tick "Disable cache" and set throttling to "Slow 4G" to see what a phone visitor sees.
- WebPageTest. The most detailed waterfall available, with connection view, priority, and the FCP/LCP lines drawn over it, from real locations and devices.
- getReport's speed test. The waterfall of the Lighthouse run, under the lab and field tables, up to 60 requests sorted by start time, each with its transfer size.
- PageSpeed Insights shows metrics, audits and a filmstrip, and its treemap shows JavaScript by size. Neither is a waterfall; there is no per-request timeline in PSI.
How getReport checks it
The speed module keeps the request list from the Lighthouse run and draws it as a waterfall: document in blue, render-blocking files in amber, images over 200 KB in red, everything else grey, with the transfer size beside each name. Four findings read the same data and name the pattern for you:
The last one comes from the best-practices module, which loads the page in Chromium and records the protocol each response used. The HTTP/2 test shows the detail as a table: the ALPN handshake result, whether HTTP/3 is advertised, the TLS version and cipher, and a chip per protocol counting the responses.

The anatomy of a bar
In DevTools, hovering a bar shows the phases. From left to right:
| Phase | What is happening |
|---|---|
| Queueing / Stalled | The browser has the request but has not sent it: waiting for a free connection (HTTP/1.1 allows six per host), for a higher-priority request, or for the connection below |
| DNS lookup | Turning the hostname into an address. Once per host per page, cached afterwards |
| Initial connection | The TCP handshake, one round trip |
| SSL / TLS | The TLS handshake, one to two round trips |
| Request sent | Sending the request headers, usually a millisecond |
| Waiting (TTFB) | Sent, nothing back yet: the server is thinking, or the network is slow |
| Content download | Bytes arriving |
DevTools draws queueing and stalling in grey, waiting for the server in green and the download in blue; the connection phases have their own colours, listed in the tooltip's legend. Two things follow. The first request to a new host always pays DNS, connect and TLS (three or four round trips, 300–600 ms on mobile) and later requests to the same host do not, which is why a page that talks to twelve hosts is slow before a single byte of content arrives. And the green phase on the first bar is the server's response time for the HTML; on every other bar it is mostly the server's time to serve a static file, which should be near zero.
The vertical lines: in DevTools, blue is DOMContentLoaded (the HTML is parsed and blocking scripts have run) and red is Load (every initial resource has arrived). WebPageTest adds First Contentful Paint, Largest Contentful Paint and, in the connection view, a line per connection. In the report's waterfall the lines are not drawn; read the LCP time from the lab table and find the bar that ends just before it.
The six patterns
1. A long first bar: the server
Sketch: the HTML bar is 1.5 s wide and mostly green; every other bar starts after it.
Nothing can start until the HTML arrives, so a slow first byte pushes every other bar right by the same amount. Causes: no page cache (WordPress rendering the page on every visit), a slow database query, an overloaded shared host, a distant server with no CDN. The TTFB finding names the number; the TTFB guide has the fixes. If the first bar is long but mostly orange and purple rather than green, it is the connection, not the server: a far-away origin or a TLS setup without session resumption.
2. A staircase: dependency chains
Sketch: a stylesheet bar, then a font bar that starts exactly where the stylesheet ends, then another; or a script, then a second script it loaded, then a fetch to an API.
Each step is a file the browser could not know about until it had parsed the previous one. CSS @import chains, web fonts referenced from a stylesheet, scripts that inject scripts, a framework bundle that fetches data before it can render. Each step is at least one round trip. Fixes: flatten @import into one file, preload the font so it starts alongside the stylesheet, preconnect to hosts you know will be needed, and render the first screen on the server so no API round trip stands between the visitor and the content. Preload, preconnect, prefetch explains which hint breaks which chain.
3. A wide gap before the big image: late discovery
Sketch: the document, stylesheets and scripts finish at 1.2 s; the hero image, the widest bar on the page, starts at 2.4 s.
The browser requests images as soon as it sees them in the HTML, so an image that starts late was not in the HTML: it was a CSS background-image (discovered only when the stylesheet is parsed and the element laid out), a lazy-loaded <img> that a script had to un-lazy, or a slider that built its slides with JavaScript. The LCP element line under the lab table tells you which image; the fix is to put it in the HTML as a plain <img> with fetchpriority="high", or to preload it when it must stay in CSS.
4. Many thin bars from one host, in batches of six: HTTP/1.1
Sketch: dozens of short bars from the same domain, each with a grey stalled phase, starting in groups; the seventh waits for the first to finish.
HTTP/1.1 allows six connections per host in Chrome, so the seventh request queues. Pages with 40 images or 20 scripts spend most of their waterfall waiting in line. HTTP/2 multiplexes everything over one connection and the batches disappear. The HTTP/2 finding counts the responses still on http/1.1; the fix is a server or CDN setting, described in HTTP/2 and HTTP/3. A third-party host still on HTTP/1.1 shows the same pattern for its own files.
5. Third-party bars in the middle of the critical path
Sketch: between the stylesheet and the first image, three bars to a tag manager, a consent platform and a chat widget, each paying its own DNS, connect and TLS.
They are not your content, but if they are scripts without defer they block the parser, and even with defer they compete for bandwidth and main-thread time before the LCP image. Recognise them by the hostname. Move them after the LCP image (load on interaction, or from the tag manager with a delay), and preconnect to the ones that must stay early. Third-party scripts covers each kind.
6. A short bar, then another for the same page: redirects
Sketch: the first bar is 200 ms with almost no download, followed by a second document bar; sometimes a third.
http:// to https://, then bare domain to www, then a trailing slash: each is a full round trip before the real request. On mobile a chain of three costs 500–900 ms before the server even starts thinking. The redirects finding lists the hops; the fix is a single rule that sends every variant straight to the final URL, and links (menus, ads, social profiles) that use the final URL directly.
Reading priority and initiator
DevTools hides two columns that explain most surprises; right-click the column header and enable Priority and Initiator.
Priority is what the browser decided the file was worth: Highest for the document and stylesheets, High for scripts in <head> and fonts, Medium or Low for images depending on whether they are in the viewport, Lowest for prefetch. An LCP image at Low priority was not in the viewport when the browser decided, or was lazy-loaded; fetchpriority="high" overrides the guess. A script at Highest that does nothing for the first paint is a candidate for defer, which drops it to Low.
Initiator is who asked for the file: "Parser" for anything in the HTML, a stylesheet's name for fonts and background images, a script's name and line for anything requested by JavaScript. A staircase (pattern 2) reads as a chain of initiators; an LCP image whose initiator is a slider script (pattern 3) tells you where to fix it. In DevTools, holding Shift while hovering a row highlights its initiator in green and its dependents in red.
A worked example
A typical shop page (the mockup store the report's fixtures are built from) reads like this:
- The document: a bar with a green phase most of its width. The server rendered the page without a cache. Pattern 1; the TTFB finding fails.
- Three stylesheets and two scripts, all amber, all starting when the document ends and finishing together. The theme's stylesheet, the builder's, a plugin's, jQuery and a slider. Pattern 5 and the render-blocking finding.
- A font file starting exactly where the theme stylesheet ends: pattern 2. The headline cannot paint until it arrives.
- The hero image, red, starting after the slider script has run and wider than anything else: patterns 3 and the image weight finding. This is the LCP element.
- Two dozen thumbnails from the same host in batches of six: pattern 4, HTTP/1.1.
- Analytics, a chat widget and a reviews script from four hosts, each with its own connection setup, interleaved with the thumbnails.
The lab LCP falls just after the hero bar ends. Every second of it is accounted for by the rows above.
What to fix, in which order
- Redirects (pattern 6). Cheapest fix, saves the most per minute of work.
- The first byte (pattern 1). A page cache or a CDN moves every bar left.
- Render-blocking files (pattern 5, amber bars).
deferon scripts; fewer, smaller stylesheets; see Render-blocking resources. - The LCP image's start time (pattern 3). Plain
<img>,fetchpriority="high", no lazy loading, or a preload. - Chains (pattern 2). Preload fonts, flatten imports, preconnect to required hosts.
- Third parties (pattern 5). Delay, facade or remove.
- HTTP/2 (pattern 4). A setting; last only because it is usually already on.
Verify
- Re-run the speed test after each change and compare waterfalls: the first bar shorter, no amber bars between the document and the hero, the hero image starting within a few hundred milliseconds of the document, no batches of six.
- In DevTools with Slow 4G throttling, the LCP image's Priority column reads High and its Initiator reads Parser.
- The HTTP/2 test's protocol table counts every response under h2 or h3.
Common mistakes
- Reading the waterfall on fast Wi-Fi with a warm cache. Symptom: every bar is short and the page looks fine. Fix: "Disable cache" and Slow 4G throttling in DevTools, or use the report's throttled run.
- Counting bars instead of reading them. Symptom: "80 requests, too many". Fix: 80 small requests over HTTP/2 can be faster than 20 over HTTP/1.1; look at the shape, not the count.
- Fixing the longest bar first. Symptom: a 3 MB video at the bottom is optimised while LCP does not move. Fix: work on the bars that finish before the LCP line; anything after it is not on the critical path.
- Mistaking a stalled phase for a slow server. Symptom: "the CDN is slow" when the bar is grey, not green. Fix: grey is queueing on the browser's side; check the connection count and priority.
- Preloading everything. Symptom: twenty
preloadhints, LCP worse than before. Fix: preload only the LCP image and the one font the headline uses; preloads compete with each other.