DEV Community

Hana
Hana

Posted on

I measured third-party weight on 51 B2B SaaS homepages. The median page is 34.6 percent someone else's code.

On 15 September 2026 I measured 51 B2B SaaS homepages with Lighthouse 12.8.2 under applied 4G throttling and grouped every network request by registrable domain. I wanted one number: how much of a modern marketing page is code the company did not write.

The median page is 6,072 KB and 34.6 percent third-party by bytes. Quartiles: 20.2 percent, 34.6 percent, 62.6 percent. The median page pulls from 29 distinct third-party domains. Median LCP under this throttling was 7.2 seconds.

Applied throttling, not simulated

This matters more than the rest of the method combined, so it goes first.

Lighthouse's default throttling-method is simulated. It loads the page unthrottled and predicts what a slow connection would have done. devtools throttling applies the restriction during the load. The docs acknowledge "inherent inaccuracy" in the simulation without saying how large it gets.

On this set it got large. I had earlier measured some of these same pages with the default and quoted the numbers to people. Directus read 50.9 seconds simulated and 2.4 seconds applied. Front read 36.9 against 2.4. Greptile read 33.3 against 4.2. Median ratio across the pages I had both readings for was 3.1x, with a tail past 20x.

Every figure below uses:

--throttling-method=devtools
--throttling.requestLatencyMs=150
--throttling.downloadThroughputKbps=1600
--throttling.uploadThroughputKbps=750
--throttling.cpuSlowdownMultiplier=4
Enter fullscreen mode Exit fullscreen mode

Which vendor costs the most

By total bytes across all 51 sites:

vendor total requests sites
googletagmanager.com 28,430 KB 217 43
qualified.com 19,478 KB 379 12
wistia.com 10,004 KB 61 4
mux.com 8,369 KB 44 3

Google Tag Manager appears on 43 of 51 sites, 84 percent. Then google.com at 39, doubleclick.net at 39, facebook.net at 36, linkedin.com at 36.

But total bytes across a set rewards reach, not weight. The more useful number is median share of page weight where the vendor is actually present:

vendor median share where present sites
mux.com 34.1% 3
wistia.com 32.9% 4
qualified.com 24.0% 12
spara.co 20.3% 3
chilipiper.com 11.0% 3
googletagmanager.com 9.8% 43

mux and wistia are video hosts delivering the site's own marketing video, so those bytes are page content that happens to be vendor-served. Excluding them, the heaviest thing per page is a sales chat widget.

The chat widget

On the 12 sites running Qualified, it outweighs Google Tag Manager on 9. Median where present: 1,783 KB across 31 requests, against GTM's 595 KB across 4. A 3.0x difference.

One file, assets.qualified.com/packs/js/widget/sandboxed/messenger-11148207.js, measured 820.2 to 821.2 KB on 11 of those 12 sites. It was the single heaviest request on the entire page on 8 sites, more than any other host in the set. The widget also ships its own 284 KB loading video and its own 222 KB webfont.

The honest framing, because I got this wrong first: Tag Manager is the more common tax, the chat widget is the larger one where it is installed. I initially generalised from braze.com, which turns out to be one of only three sites where GTM wins head to head (2,039 KB against 1,518 KB). Generalising from it inverts the per-page result.

What deferring it is worth

I took greenhouse.com, the heaviest case, and deferred the widget to first user intent rather than removing it. Loader injected into the live response, so the only variable is when the vendor script runs. Two runs each:

LCP page requests vendor
unchanged 21.92 s / 22.06 s 5,479 KB 349 1,825 KB
deferred 13.52 s / 13.46 s 3,648 KB 315 0 KB

8.5 seconds. A control with the vendor hard-blocked landed at 13.33 s, the same place, so the deferral is complete rather than partial.

One result that went the other way, which is the more useful half. With a 15 second idle fallback armed instead of waiting for intent, the same page measured 29.22 s and 29.60 s, worse than changing nothing. That page's LCP does not settle until around 22 seconds on a 1.6 Mbps link, so the timer fired in the middle of the work deciding LCP and dropped 1.8 MB on top of it. If you defer a widget on a timer, the timer has to fire after your page has actually finished painting, and on a slow connection that is far later than it feels.

The judgment calls, because they change the answer

Owned-asset CDNs are not third parties. cdn.prod.website-files.com is Webflow's asset host and carries the site's own images. It totalled 27,995 KB across the set, the single largest host in the raw data, and counting it as third party would have been the biggest error available. Same for framerusercontent.com, squarespace-cdn.com, ctfassets.net, sanity.io, storyblok, and github.githubassets.com, without which github.com reads as 97 percent third party instead of its true 0.0 percent.

Generic CDNs are judged per site. cloudfront, fastly, jsdelivr, unpkg and friends: if 90 percent or more of their bytes on that page are Image, Font, Stylesheet or Media, they count as owned assets. Otherwise they are shipping scripts and stay third party.

Video hosts were not reclassified. wistia and mux are third party even though they deliver page content, because the site chose a vendor and could choose another. They top the median-share table and that is called out rather than hidden, so "highest median share" is not misread as "heaviest tracker".

Null is not zero. 12 of 51 sites returned a null Lighthouse performance score, every one with NO_TTI_CPU_IDLE_PERIOD. That means the page never reached idle, not that it scored 0. They are recorded as null with the reason and their network data is included. Rounding a null to 0 sorts fast pages to the top of a worst-first list, which is exactly the mistake I made before catching it.

695 of 11,170 requests reported transferSize 0 (cache, aborted, data URIs). They contribute no bytes but are still counted as requests.

braze.com and edisonmail.com each failed once, PAGE_HUNG and a Chrome protocol error, and succeeded on identical retries. Both are reported from the successful run. No site is missing and none needed a third attempt.

What I would do with this

If you run a chat widget, measure what it costs on your own page before you defend it, and measure with applied throttling or the number will not mean anything. If it is over a megabyte, deferring it to first scroll or first tap is a small change with a large effect, and a locally drawn placeholder bubble means the visitor never notices the difference.

If you ship a Tag Manager container, 9.8 percent of the median page is not the emergency, but 43 of 51 sites carry it and almost nobody audits what accumulated inside it.

The raw data

Everything above is reproducible from the dataset, and the dataset is free with nothing gated:
the per-site breakdown, all 326 third-party vendors aggregated, and the findings file. It includes the judgement calls in full, because a different call on Webflow's asset CDN alone would move the headline number by a lot.

I also sell one fix built from this: a drop-in loader that defers a chat widget to first user intent, measured at 21.9 to 13.5 seconds on greenhouse.com. Its listing carries the runs above that argue against it, including the fallback that made the same page worse. hanaharness.gumroad.com/l/widget-defer

Top comments (1)

Collapse
 
perennova profile image
Sébastien

The applied-vs-simulated throttling point deserves more attention than it usually gets.

Thank you for actually running it that way, and for keeping the null scores separate
instead of rounding them into the data.

One question about the CPU side, since that's where I'd expect the numbers to get worse
rather than better. Lighthouse's applied throttling defaults to a 4x CPU slowdown, which
roughly emulates a mid-tier phone, not an old one. A six-year-old Android is closer to
8-10x. Did you try any of the 51 at a higher multiplier, and did the third-party share
behave differently ?

I'm asking because of the detail that stuck with me most: the 12 sites where the page
never reached idle. A page that never idles keeps the radio and the CPU awake, and on
a device whose battery is already degraded that's not just a metrics problem, it's part
of what makes the phone feel old enough to replace. The digital's footprint in France puts user devices at 65-92% of the total depending on the
indicator, dominated by manufacturing, so anything that shortens a smartphone's usable life
costs far more than the transfer itself ever did.

Which makes your 29-third-party-domain median a device-lifetime number as much as a
performance one. Have you seen anyone report third-party weight that way?