Home › Guides › HTML Weight and Render-Blocking Scripts

HTML Weight and Render-Blocking Scripts

60 homepages weigh 51 MB. The median is 414 KB, 60% still block the parser on a script, and one site ships a single 12.8 MB JSON file inside its HTML.

How this was measured

On 2026-10-17 we fetched the homepage of each of the same 78 hand-picked domains our other surveys use — news, commerce, SaaS, developer tooling, social, streaming, reference, SEO, education, finance, cloud, local and government — and stored the raw HTML. This article re-parses that stored HTML offline; no network request was made to produce any number below.

62 of the 78 returned a readable document and 60 of those are homepages. Sixteen never did: six answered 403 (ebay.com, stackoverflow.com, npmjs.com, searchengineland.com, yelp.com, nih.gov), six failed to connect at all (nytimes.com, reuters.com, washingtonpost.com, etsy.com, quora.com, coinbase.com), two answered 202 (amazon.com, booking.com), one answered 406 (uber.com) and archive.org answered 200 with an error page. Two more — khanacademy.org and paypal.com — returned a bot-challenge page and were discarded rather than parsed. Of the 62 usable responses, 42 came over a direct connection and 20 through a proxy.

Three definitions decide every number below, and they are not the ones most tools use.

  • A byte is a UTF-8 byte. We measure Buffer.byteLength(html, "utf8"), not JavaScript's .length, which counts UTF-16 code units and undercounts any page containing Chinese, emoji or accented text by a factor of three or four. These are also decompressed bytes: the snapshot does not store transfer size, so nothing here is comparable to a Lighthouse "transfer size" figure, and it is not what your visitor downloads over the wire.
  • Inert containers are masked out before anything is counted: comments, <script>, <style>, <template> and <noscript> content are replaced with spaces of equal length, so a tag inside a JavaScript string, a commented-out block or a client-side template is never counted. <noscript> matters more than it sounds — with JavaScript enabled its contents are raw text, and the fallback scripts inside it never load. Our first pass counted them and inflated several sites.
  • "Render-blocking" has exactly one meaning here: an external script that has a src, is not type="module", and has no defer, no async and no nomodule. Modules are excluded because they are deferred by default; nomodule is excluded because current browsers skip those entirely. Missing any one of those four exclusions invents defects that do not exist — our first pass counted every module as blocking.

Ten synthetic assertions lock the parser down before it touches real data (--selftest): a module that must not block, a bare <script defer> written with no value, a data-src that must not be read as a real src, a JSON-LD block that must not be counted as executable JavaScript, a script inside <noscript> that must not be counted at all, and 1,000 Chinese characters that must measure 3,000 bytes rather than 1,000.

Finding 1: 60 homepages weigh 51 MB, and the median is 414 KB

The 60 homepages total 51.23 MB of HTML. The median is 413.9 KB, the first quartile 135.7 KB, the third 891.4 KB and the 90th percentile 1.72 MB. The spread is not a bell curve: 11 sites (18.3%) exceed 1 MB, 26 (43.3%) exceed 500 KB, and 11 (18.3%) come in under 100 KB — so "the median homepage" describes almost no individual homepage.

The extremes are both real and both instructive. reddit.com ships an 8.2 KB homepage containing 67 elements and a single script tag: the entire product arrives later, over JavaScript. bloomberg.com ships 14.05 MB, which is more than the other 59 homepages' first quartile combined. Neither is a bug; they are two ends of the same decision about how much work the document does.

Finding 2: about half of the median document is inline

Across the sample, inline content — script bodies and <style> blocks — accounts for a median of 47.9% of each document, and for 70.8% of all 51.23 MB pooled. The gap between those two numbers is the usual one: a handful of enormous pages dominate the pooled figure, so we report both and treat the per-site median as the headline.

Broken down by kind, the per-site medians are 4.5% of the document for executable inline JavaScript (median 17.3 KB), 5.4% for inline CSS (median 8.9 KB) and 0% for inline JSON-LD (median 0.3 KB). 29 sites are at least half inline and 15 are at least 70% inline.

The most extreme cases are worth naming because they are not all the same problem. youtube.com is 929.6 KB of which 895.9 KB (96%) is one inline script; figma.com is 1.72 MB with 1.44 MB of inline JavaScript; cnn.com is 5.53 MB carrying 2.42 MB of inline JavaScript and 2.12 MB of inline CSS in a single <style> block. Those are genuine payload decisions. The largest of all is not, and it is the subject of the next finding.

Finding 3: the biggest file in the sample is not JavaScript

bloomberg.com's 14.05 MB homepage is 91% one tag: <script id="__NEXT_DATA__" type="application/json">, weighing 12.82 MB. It is Next.js's hydration payload — the serialised route data the client framework reads after the document loads. It is not parsed as script, it never blocks the parser, and no JavaScript minifier will ever touch it.

That makes it invisible to most optimisation work, which is why we split it out as its own category. 23 of the 60 sites ship an inline data block of this kind (application/json, text/template and similar). Among those 23 the median block is 64.1 KB, or 15.4% of that site's document. Pooled, data blocks account for 15.75 MB — 30.7% of everything we measured.

The leaderboard: bloomberg.com 12.82 MB (91% of its own document), wise.com 633.5 KB (71%), pinterest.com 619.7 KB (49%), airbnb.com 372.5 KB (64%), walmart.com 267.6 KB (62%), stripe.com 228.9 KB (32%), target.com 190.4 KB (47%) and spotify.com 185.6 KB (61%). Three sites ship a single data block larger than the median entire homepage (413.9 KB) — bloomberg, wise and pinterest.

The practical consequence is that a page can be "fully optimised" by every script-level measure and still weigh a megabyte, because the weight was never in a script. If you are auditing a slow page, weigh the JSON separately before you conclude anything about bundling.

Finding 4: 60% of these homepages still block the parser, and the distribution is bimodal

412 external scripts across the sample block parsing — a median of 1 per site and a maximum of 99 on twitch.tv. 36 of the 60 sites (60%) have at least one. Only 69 of the 412 sit in the <head>; the other 343 are in the body, where they still block the parser at the point they appear but no longer delay the head.

The shape of the data is the interesting part, and it is not a gradient. 24 sites have zero blocking scripts and 36 have one or more, with very little in between. Of the 24 clean sites, 22 got there by deferring — walmart.com writes defer on 114 external scripts, stripe.com on 82, target.com on 61, doordash.com uses async on 239 — and only two are clean because they barely have external scripts at all. On the other side, 20 of the 36 sites with blocking scripts also use defer, async or type="module" somewhere: they know the mechanism exists and applied it to part of the page.

18 sites use none of the three anywhere, and those 18 account for 280 of the 412 blocking scripts (68%). Put differently: two thirds of the blocking in this sample is concentrated in under a third of the sites, and those are the sites that never reached for the attribute at all. Adoption, not tuning, is what is missing.

Finding 5: 40% of head blocking scripts belong to somebody else

Of the 69 blocking scripts that sit in the <head> — the position that costs the most — 28 (40.6%) load from a third-party domain, spread across 17 sites. The most frequent host is cdn.cookielaw.org with 7 occurrences, which is OneTrust's consent manager, followed by static.licdn.cn (3), cdn.optimizely.com (2), and one each from cdn.ketchjs.com, geolocation.onetrust.com, assets.bwbx.io, a.slack-edge.com and reveal.clearbit.com.

docker.com is the clearest example of what that costs: its two head blocking scripts are <script src="https://cdn.cookielaw.org/scripttemplates/otSDKStub.js"> and an Adobe DTM loader. A consent banner that must run before anything else is a legitimate reason to block, but it is worth noticing that the thing delaying first paint on a developer-tools homepage is a cookie notice and a tag manager, not the product.

Finding 6: the first blocking script appears 3% into the document

Among the 36 sites that block, the first blocking script appears a median of 3% of the way into the document (first quartile 1.2%, third quartile 42.2%). 23 sites place it within the first 10% and 25 within the first 25%.

In absolute terms the earliest are screamingfrog.co.uk at byte 130 of a 195.4 KB document (0.1%), yoast.com at byte 234 of 218.8 KB (0.1%), netlify.com at byte 329 of 405.4 KB (0.1%), mozilla.org at byte 405 of 46.7 KB (0.8%) and python.org at byte 780 of 50.7 KB (1.5%). These are not mistakes — an analytics or consent snippet is meant to run early — but it does mean the parser stops within the first few hundred bytes on a quarter of the sample.

Finding 7: the worst offenders are frameworks enqueuing one file at a time

The ten sites with the most blocking scripts are twitch.tv (99), docker.com (46), airbnb.com (41), heroku.com (32), who.int (27), semrush.com (22), slack.com (16), yoast.com (14), mozilla.org (11) and harvard.edu (10). We printed the original tag behind every one of them, and three distinct patterns emerged.

docker.com and heroku.com are both WordPress, and both enqueue a plugin's entire asset list on every page. docker.com loads 46 blocking scripts, and after the consent and tag-manager pair they are almost all SyntaxHighlighter brushes: shCore.js, shBrushAS3.js, shBrushArduino.js, shBrushBash.js, shBrushColdFusion.js, shBrushClojure.js, shBrushCpp.js, shBrushCSharp.js, shBrushCss.js, shBrushDelphi.js and on through the alphabet — every language the plugin can highlight, loaded on a homepage that highlights nothing. heroku.com does the same with jQuery, jQuery Migrate, Masonry, wp-hooks, wp-i18n, Parse.ly and nine Elementor modules.

who.int loads 27 blocking scripts, most of them ASP.NET ScriptResource.axd handlers with opaque query strings, plus the entire Kendo UI bundle (kendo.all.min.js and kendo.timezones.min.js) from a CDN. That is a server framework composing script references at runtime rather than a build step producing one file.

twitch.tv's 99 are all in the body, all served from assets.twitch.tv with crossorigin="anonymous", and every one is a webpack chunk named by numeric hash — 4405-7c085527b29fdf128ce1.js, 89570-b04be00b30f5b4f61598.js and so on. A bundler produced these; nobody chose to block on them. The fix is one attribute applied by the same tool that emitted the tags.

Finding 8: stylesheets — 59 links that block nothing, and 42 that all block

394 <link rel="stylesheet"> tags across the sample, a median of 4 per site, a maximum of 59. 8 sites ship no stylesheet link at all (fully inlined or fully JS-injected), and 21 sites ship five or more. Because a stylesheet blocks rendering by default, this is the other half of the same problem — but the count alone is misleading in both directions.

squareup.com has 59 stylesheet links and zero render-blocking ones. Every one is written as <link rel="stylesheet" disabled media="(max-width: 0)" href="…ModalPartial.XHC6VJIM.css"> — SvelteKit emitting one file per component, each marked disabled against a media query that never matches, to be switched on by JavaScript when that component mounts. It is a deliberate, correct implementation of "load CSS with the component", and any tool that counts <link rel=stylesheet> and calls it blocking will report 59 problems where there are none.

github.com is the opposite: 42 stylesheets, all blocking, each with media="all" — separate light, dark and high-contrast sheets for many components. We treated media="all" and media="screen" as blocking, which is correct for this measurement even though a browser only applies the sheets whose conditions match. 72 stylesheet links across 13 sites carry a media query that does not match a desktop viewport (media="print" and similar), and those genuinely do not block.

Finding 9: DOM size is where the weight ends up

The median homepage contains 1,104 elements after masking (first quartile 746, third quartile 2,079), and the maximum is 6,430 on atlassian.com. <div> accounts for a median of 24.4% of them, with a median of 318 divs and a maximum of 2,058 on theguardian.com.

These numbers are a floor. They count what the server sent, and everything a client framework appends after hydration is missing from them — which is exactly why reddit.com can weigh 8.2 KB and bloomberg.com 14.05 MB while both render a full application.

Finding 10: heavy pages are not the blocking pages

Splitting the sample into volume quartiles produces the result we least expected. The lightest quartile (8.2 KB–119.3 KB) has a median of 3 blocking scripts and the second quartile (135.7 KB–405.4 KB) a median of 6 — but the heaviest two quartiles both have a median of zero.

The explanation is in the tags: the big pages are the ones with a build step, and a build step emits defer, async or type="module" by default. The small pages are the ones assembled by a CMS plugin system, where each contributor adds one more <script src> with no attributes. Weight and blocking are measuring two different generations of web development, and only one of them is getting worse.

What to ship

Weigh the JSON separately. A third of all bytes in this sample are inline data blocks, they are invisible to script-level tooling, and the single largest file we found is one of them.

Add defer to every non-critical external script, or let your bundler do it. 18 sites here use none of the three mechanisms and produce 68% of all blocking. If you have a build step you probably already have this; if you have a plugin system, you almost certainly do not.

Count your head blocking scripts and name their owners. 69 scripts block in the head and 28 of them are third-party. The consent manager and the tag manager are usually the answer, and the question worth asking is whether they must run before first paint.

Audit plugins, not pages. Both WordPress sites in the worst ten are loading a plugin's complete asset list on a page that uses none of it. One enqueued brush set is 40 scripts.

Do not trust a stylesheet count. squareup.com has 59 and blocks on none; github.com has 42 and blocks on all of them. Only a media attribute or a disabled attribute tells you which.

Report per-site medians alongside pooled totals. The pooled inline share here is 70.8% and the per-site median 47.9%, because three sites hold a third of the bytes. Either number alone misleads.

The resource hints survey measures the other half of this problem — preload and preconnect, and the 110 preconnect origins that cannot be found in the static HTML — and the JSON-LD survey covers the structured-data blocks that contribute to these byte counts. Our heading analyzer parses a pasted page statically the same way this survey did: from source, with no rendering, so what you see is what the server sent.

Limitations

These are decompressed bytes and they are not transfer size. The snapshot stores the HTML text but not the compressed stream, so nothing here can be compared with a Lighthouse or DevTools transfer figure. A page that is 14 MB uncompressed may be a fraction of that on the wire — and a page that is 8.2 KB uncompressed may still pull megabytes of JavaScript afterwards.

Static HTML only, and the biggest pages are the least complete. Everything a client framework appends after hydration is invisible to this method: script tags, stylesheet links, DOM nodes, all of it. The counts in this article are floors on what the server sent, and for a React or Svelte homepage they are a small fraction of what the browser ends up with.

"Blocking" is a static verdict about the document, not a measurement of load time. We classify a script by its attributes; we never measured a single page's LCP, INP or time to interactive. A blocking script on a fast CDN may cost less than a deferred script on a slow one, and we cannot tell you which.

The media judgement is a desktop-viewport assumption. We treat media="all", media="screen" and an absent media as blocking. On a print or non-screen context the answer differs, and we did not model it.

One snapshot, fetched on 2026-10-17, from an exit IP in Asia, over two network paths (42 direct, 20 proxied). Regional variants serve different markup and different asset lists, and 16 of 78 domains never answered — several of them WAFs blocking our network rather than site failures, which biases the sample towards sites permissive of crawlers.

Reproduce it

The script and the shared domain list ship with this site. node scripts/survey-resource-hints.mjs fetches the homepages and writes .workbuddy/tmp/resource-hints-survey/raw.json; node scripts/survey-html-weight.mjs --raw=.workbuddy/tmp/resource-hints-survey/raw.json re-derives every number in this article from that file without touching the network. Pinning the snapshot matters: run without --raw and the script picks the newest stored snapshot by modification time, whose numbers will not match these. --selftest asserts the parser against ten synthetic documents first, including a type="module" that must not count as blocking and a <noscript> script that must not count at all. --evidence prints the original tag behind every verdict, with --evidence=blocking, =big, =data, =head, =css and =sizes narrowing it to one class of finding; every line carries its domain.

About the author

Hongtao Ren (任宏涛) — Developer based in Xi'an, China. Builds browser-based tools and JetBrains IDE plugins. He built and maintains SerpPrism.

Corrections are the most useful thing you can send. If a tool or guide here gives you a wrong answer, that is a bug, not a judgement call — use the contact page.

Questions

How big is the average homepage's HTML?

The median across 60 homepages is 413.9 KB of decompressed HTML, with a first quartile of 135.7 KB and a third quartile of 891.4 KB — the spread is wide enough that the median describes almost no individual site. 11 sites exceed 1 MB and 11 come in under 100 KB. The largest, bloomberg.com at 14.05 MB, is 91% one inline JSON block; the smallest, reddit.com at 8.2 KB, is a shell that renders everything later.

How many sites still use render-blocking scripts?

36 of 60 homepages (60%) have at least one external script with no defer, no async, no nomodule and no type="module" — 412 scripts in total, a median of 1 per site and a maximum of 99 on twitch.tv. The distribution is bimodal rather than gradual: 24 sites have zero, and the 18 sites that use none of the three deferral mechanisms anywhere account for 280 of the 412 (68%).

Is a big HTML file the reason a page blocks?

No, and in this sample the relationship is inverted. Split into volume quartiles, the lightest quartile has a median of 3 blocking scripts and the second a median of 6, while the two heaviest quartiles both have a median of 0. Large pages tend to be built by a bundler that emits defer or type="module" by default; small pages tend to be assembled by a CMS where each plugin adds one plain script tag.

What is a hydration payload and why does it matter for page weight?

It is the serialised route data a client framework reads after the document loads, usually written as an inline