What 60 real Lighthouse runs say about what actually breaks LCP
Published · 11 min read · RankVitals engineering
Every performance article, including our own, gives you the same list: render-blocking resources, oversized images, third-party scripts, cache headers. The lists are correct. What they never tell you is which items on them actually distinguish a fast page from a slow one — because almost nobody publishes the distribution, only the advice.
So we ran the experiment. Sixty Lighthouse runs against the sixty most-visited domains on the public web that will answer a mobile browser with an HTML page, using the same engine and the same throttling profile our product runs by default. Then we asked one question of the data: of all the audits that fail, which ones fail on the slow sites and *not* on the fast ones?
The answer was not what the checklists imply. The most commonly-failed audits — render-blocking resources and cache lifetimes — fail at almost exactly the same rate on the fastest sites in the sample as on the slowest. They do not discriminate at all. What discriminates is much blunter: how many bytes of JavaScript you shipped and did not use.
Methodology
Corpus construction was mechanical, so you can rebuild it. We took the Tranco top-1M list generated on 9 August 2026, walked it from rank 1, and kept every domain whose https:// root answered HTTP 200 with an HTML content type to a mobile user agent — skipping the infrastructure and API-only domains that dominate the top of any popularity list. Reaching sixty qualifying sites took us to rank 103.
Each site got one Lighthouse 13.4.1 run of the performance category — the same major line the worker pins — driven by a freshly launched headless Chrome that is killed afterwards, exactly as our production worker does it. Settings mirror our default mobile test: mobile form factor at 412×823 with a 2.625 device pixel ratio, simulated throttling at 10 Mbps down / 5 Mbps up, 30 ms RTT, no CPU slowdown multiplier. All sixty runs completed. Ten of them needed a second attempt after a failure on our machine rather than theirs, which is a decent live illustration of why we treat any single run as an estimate.
One deliberate omission: none of this is customer data. We could have queried our own production database for aggregates, and the honest reason we did not is that a public corpus is reproducible and a private one is not — you can re-run every number below, and you cannot re-run ours. No customer URL, test or identifier appears anywhere in this study.
The distribution
First the shape of the sample, because it decides how much weight the rest deserves. These are the most-visited sites on earth. Many have dedicated performance teams. This is a floor, not an average web page.
- Performance score: median 93, mean 78.8. Thirty-one of sixty scored 90 or above, twenty-one landed between 50 and 89, and eight scored under 50. Seventeen scored a perfect 100.
- LCP: median 2.00 s, 75th percentile 3.62 s, 90th percentile 4.50 s, worst 11.79 s. Twenty-two of the fifty-six runs that produced an LCP value were over the 2.5 s threshold; eight were over 4 s.
- TBT: median 55 ms, 75th percentile 317 ms, 90th percentile 1,132 ms. Nineteen sites exceeded 200 ms.
- CLS: median 0.005, 75th percentile 0.115. Fourteen sites exceeded 0.1 — and ten of those fourteen also failed the unsized-images audit.
- Page weight: median 2.30 MB, 90th percentile 7.24 MB, worst 18.03 MB. Requests: median 79, 90th percentile 187, worst 390.
- TTFB: median 118 ms, 90th percentile 325 ms. Servers are not the problem at this tier of the web.
The single most striking number is not in that list. Every single one of the sixty sites failed at least one audit at high impact, and the median site failed six of them. There is no such thing as a clean Lighthouse run at scale — not on the sixty most-resourced sites on the internet. If your report is showing failures, that is the normal condition, and the question is never "how do I get to zero", it is "which of these six actually costs me a second".
What fails most often — and why that list is misleading
Counting how many of the sixty sites failed each audit gives the list everyone expects, with the number of sites affected and, in brackets, how many of those were at high impact:
- Document latency (server response, redirects, compression on the main document) — 49 sites (48 high).
- Network dependency tree (long chains of requests that must resolve before content can paint) — 48 (48 high).
- Unused JavaScript — 48 (37 high), averaging 486 KB of unused script per failing site.
- Render-blocking resources — 43 (34 high).
- Cache lifetimes — 40 (23 high).
- Unused CSS — 40 (20 high), averaging 95 KB.
- Image delivery (format, sizing, compression) — 34 (17 high).
- Forced reflow — 33 (33 high). Back/forward cache ineligibility — 33 (33 high).
- Images without dimensions — 30. Excessive total byte weight — 27.
failure rate on slow sites (LCP > 2.5s, n=22) vs fast sites (n=34)
render-blocking resources slow 73% fast 79% <- no signal
cache lifetimes slow 68% fast 74% <- no signal
forced reflow slow 59% fast 59% <- no signal
document latency slow 91% fast 85% <- weak
unused JavaScript slow 95% fast 79%
image delivery slow 73% fast 53%
LCP phase breakdown slow 36% fast 12% <- strongestRead that table again, because it is the whole point of the study. Render-blocking resources — the first item on every performance checklist ever written, including ours — failed on 73% of the slow sites and 79% of the fast ones. Cache lifetimes: 68% versus 74%. Forced reflow: 59% versus 59%, identical. These audits are not wrong and they are not useless; they are simply table stakes. Nearly everyone fails them, including the sites loading in under two seconds, so seeing them in your report tells you almost nothing about why your page is slow.
This is the trap in a flat audit list: the items at the top are the ones that fail everywhere, which is exactly what makes them poor diagnostics. Sorting audits by how commonly they fail sorts them by how little they explain.
What actually separates fast from slow: bytes and main-thread time
Splitting the sample at the 2.5-second LCP threshold and comparing medians makes the real difference obvious, and it is not subtle:
- Unused JavaScript: 521 KB on slow sites versus 159 KB on fast ones — a 3.3× gap.
- Total page weight: 4.71 MB versus 1.78 MB — 2.6×.
- Request count: 144 versus 55 — 2.6×.
- Total Blocking Time: 306 ms versus 25 ms — a 12× gap, the largest in the dataset.
- Time to First Byte: 182 ms versus 98 ms — 1.9×, and both comfortably fine. The servers are not the story.
- Median performance score: 70 versus 100.
The mechanism is boring and completely consistent with how LCP is defined. LCP is the moment the largest element in the viewport finishes painting. Under simulated throttling, that moment is pushed back by two things: bytes that have to arrive before it can paint, and main-thread work that stops the paint from happening once they have. The slow half of this sample ships two and a half times the bytes and blocks the main thread twelve times longer. Everything else — the render-blocking stylesheet, the missing cache header, the un-lazy-loaded image below the fold — is real, fixable, and mostly noise next to that.
The 12× TBT gap deserves particular attention because TBT is not an LCP metric. It measures main-thread busyness, and the fact that it separates fast from slow LCP better than any LCP-specific audit tells you where the time is going: not into the network, into script execution. A page that ships 500 KB of unused JavaScript does not just pay for the download — it pays to parse and compile it, on the main thread, in front of your hero image.
The one LCP-specific audit that does discriminate
Lighthouse 13 ships an LCP phase breakdown that splits the metric into time-to-first-byte, resource load delay, resource load duration and render delay. It failed on 36% of slow sites versus 12% of fast ones — the sharpest ratio of any audit in the set, and the only LCP-named audit that carries real signal.
That matters because it is the audit that tells you *which* phase to attack, which is the question the flat list cannot answer. A large load delay means the browser found out about your LCP image late — it was discovered by a script, or hidden behind a lazy-loading attribute it should never have had. A large load duration means the image itself is too big. A large render delay means the bytes arrived and the main thread was too busy to paint them, which is the unused-JavaScript problem wearing a different hat. Same metric, three completely different fixes.
Its sibling, LCP discovery, failed at 32% on slow sites and 38% on fast ones — no signal, which is a slightly reassuring finding: preloading and fetchpriority hygiene appear to be roughly as neglected everywhere, and it is not what is costing the slow sites their seconds.
<!-- load delay: make the LCP image discoverable in the initial HTML -->
<link rel="preload" as="image" href="/hero.avif" fetchpriority="high">
<!-- render delay: stop shipping script the first paint does not need -->
<script src="/analytics.js" defer></script>CLS is still an unsized-image problem
Fourteen sites blew the 0.1 CLS threshold, and ten of those fourteen also failed the unsized-images audit. That is a decade-old finding that refuses to die, on the most-visited sites in the world, in 2026. The fix has not changed either: width and height attributes on every image so the browser can reserve the box before a byte arrives, and an explicit min-height or aspect-ratio on any container that fills in later — ad slots, embeds, hydrating widgets.
The median CLS across the whole sample was 0.005, so this is genuinely a tail problem rather than a universal one. But it is a tail with a two-attribute fix, which makes it the highest return-on-effort item in this entire study.
What this study cannot tell you
It is lab data, one run per site, from one location, with simulated throttling. Lighthouse itself has run-to-run variance, and we did not average multiple runs — the medians above are medians across sites, not across repeats. Treat any individual number as ±10% and the shape of the distribution as the durable part.
It is also a biased sample, deliberately and unavoidably. The Tranco top 103 is not the web; it is the best-resourced corner of it. If the sixty most-visited sites on earth median at 2.3 MB and 79 requests, with every one of them failing at least one high-impact audit, a typical business site is not doing better. Read every number here as an optimistic bound.
And it measures homepages only, which are the pages most likely to have received performance attention and least likely to look like the templated product or article pages where most traffic actually lands. The comparison we did *not* make — homepage versus deep page on the same domain — is the obvious next study, and we will run it.
Finally, lab and field disagree for good reasons: field data is the 75th percentile of real users over 28 days on real devices, and it is the number Search Console judges you on. Our Core Web Vitals checker reads the field numbers straight from CrUX if you want to see both sides for your own domain, and how we compare to PageSpeed Insights covers where the two data sources come from.
The one-paragraph version
If you take one thing from sixty runs: stop triaging your Lighthouse report top-down. The audits at the top of the list fail on fast sites too. Find the bytes you are shipping and not using, find the main-thread work happening before your hero paints, and fix the LCP phase the breakdown audit points at. That is where the seconds are. A free test on your own URL gives you the same audits with the actual offending files attached — and pairs it with a 57-check SEO and AEO crawl, which is the half of this that starter templates ship broken.
FAQ
- Why did you not use your own customers’ test data?
- Because a public corpus can be re-run by anyone and a private one cannot. Aggregates from our production database would have been larger and less verifiable, and would have carried a privacy question we did not need to open. Every number here comes from sixty public homepages named by a reproducible rule, and no customer URL, test or identifier appears in the study.
- Does “no CPU slowdown multiplier” make this too optimistic?
- For mobile users, yes — a real mid-range phone is meaningfully slower than the desktop CPU behind these runs, and the standard Lighthouse mobile preset applies a 4× slowdown. We used our product’s default Broadband profile so the numbers match what a user sees in our own reports. The effect is that TBT and the slow tail here are conservative: on a 4× CPU the gap between the fast and slow halves gets wider, not narrower.
- Is 60 sites enough to conclude anything?
- It is enough for the effect sizes we are reporting and not enough for anything subtler. A 3.3× difference in unused JavaScript and a 12× difference in blocking time between the two halves are not going to be sampling noise. A five-point difference in a failure rate would be, which is why we called render-blocking and cache lifetimes “no signal” rather than trying to rank them.
- How do I find unused JavaScript on my own site?
- The Lighthouse audit lists it per file with the wasted bytes, which our reports carry through with the offending URLs attached. In Chrome DevTools the Coverage panel gives the same picture interactively. The usual causes, in order of frequency: a UI library imported wholesale for three components, polyfills for browsers you no longer support, and analytics or experimentation bundles loaded on every route for a feature used on one.