What the WooCommerce homepage already gets right
Start with the list of things this page does not need to be told, because it is longer than the list of things it does.
The hero image, the largest element on both phone and desktop, carries fetchpriority="high"
and a srcset. 44 of the 63 images in the markup carry loading="lazy". Images are served
as WebP to browsers that ask for it, from the same .jpg and .png URLs, through content
negotiation (vary: accept). The non-critical stylesheet is loaded with the
preload-then-swap pattern and a <noscript> fallback. Both web font families use
font-display: swap with metric-matched fallback faces (size-adjust, ascent-override),
which is why the mobile layout shift is exactly 0.
And the icon font is split in a way most sites never bother with. Font Awesome’s regular
weight is declared as two files with different unicode-range values: a 15 KB “core” with
the common glyphs and a 339 KB “extra” with the rest. A phone rendering this homepage
downloads only the core file. The desktop layout uses a glyph outside it, so desktop fetches
both. Either way, the browser decides, and it decides correctly.
Still, in the 5 October run, the first pixel on a throttled phone appeared at 4.4 seconds.
The hero image is not what is slow
In the same 5 October run, Lighthouse’s simulated LCP on mobile was 11.4 seconds, and that run landed in the early paint band (see the limits below: on this page, later runs reach 16 to 24 seconds without any change to the page). The observed trace from that run, recorded without throttling, shows how little of it is the image itself:
| LCP phase (observed, unthrottled) | Original |
|---|---|
| Time to first byte | 11.2 ms |
| Load delay | 63.1 ms |
| Load duration | 19.0 ms |
| Render delay | 199.7 ms |
The 42 KB hero image has finished downloading 93 ms after navigation starts. Better image delivery would not change much here. What a slow phone waits for is everything the browser must parse before it is allowed to paint, and on this page that queue starts with stylesheets.
44 KB of render-blocking CSS for six icons
The resource inventory from the mobile run:
| Type | Requests | Bytes |
|---|---|---|
| Images | 37 | 3,026,396 |
| Scripts | 70 | 1,303,372 |
| Fonts | 4 | 794,162 |
| Stylesheets | 11 | 105,372 |
| Document | 1 | 53,671 |
Of the eleven stylesheets, the largest is not the theme. It is Font Awesome’s all.min.css:
43,967 bytes over the wire, 168,695 unpacked, the first stylesheet in the head and
render-blocking. The site’s main concatenated CSS bundle, next in line, transferred 38,449.
The stylesheet defines 5,380 custom-property entries for icons. The server-rendered markup of
the homepage contains eight <i> icon elements, using six distinct icons:
<i class="fa-chevron-right … fa-regular …"> ×3
<i class="fa-regular fa-arrow-left">
<i class="fa-regular fa-arrow-right">
<i class="fa-regular fa-circle-check">
<i class="fa-regular fa-life-ring">
<i class="fa-regular fa-lock">
So the subsetting was done once, carefully, on the font, and not done at all on the stylesheet that points to it. The font split saves a phone more than 300 KB, and the browser still cannot draw anything until it has parsed the declarations for every icon the library ships.
It is not the only render-blocking stylesheet, and the head also carries 73,978 bytes of
inline <style> in our version of the page. The icon stylesheet is simply the one where the gap between what is shipped and
what is used is easiest to see.
245 KB of italic for the word “your”
Three variable fonts arrive on the phone: ProximaVara (305,745 bytes), Inter roman (227,605)
and Inter italic (245,461). The third is the curious one. The theme declares italic Inter as
its own file, and the browser fetches it only when it actually renders italic text in that
family. In the homepage markup, the italic text is one word, wrapped in <em>, in the hero
caption: “Sell and scale your way”. The caption appears in two places in the markup, and
it is the same word both times.
Because the face uses font-display: swap, this does not block text from appearing. It
does put a quarter of a megabyte on the same connection, during the same seconds, as the
hero image and the scripts that follow.
What our proxy got wrong here
We are keeping this page in the series because our first comparison on a phone went wrong in two ways, and both are worth knowing before you compare two runs of your own page.
The first run on 5 October showed our version adding preload hints for the icon fonts it found declared in the stylesheet, including the “extra” and “brands” files. A preload is an instruction to fetch now, regardless of whether a glyph from that file will ever be drawn. On a phone, where the original page correctly skipped both, that meant 449,532 extra bytes of fonts. The unicode-range split that the WooCommerce team built was undone by a hint that was meant to help. We changed the proxy to never preload icon fonts and measured again: our version now fetches the same four font files as the original.
The paint did not recover. The hero image still waited about two seconds between arriving and being painted on our version, against 0.2 seconds on the original, exactly as it had before the fix. So the font preload was a real defect and not the cause of the slowdown.
The slowdown had no cause in our version. On a phone, this page paints its first frame at one of
three moments, about 0.25, 1.3 or 2.3 seconds in, and it does so on the untouched original as
well. The 5 October comparison had caught the original in the early band and our version in a
late one. Measured again on 19 October, 14 runs of the original and 15 of our version taken in
turn, our version’s median mobile score was 59 against 44.5, and its median LCP 16.1 seconds
against 20.0. If you maintain a preload list by hand, check it against every font that is
declared with unicode-range. And before blaming a change for a slower paint, check that the
two runs you are comparing started painting at the same moment.
Why this is not a WooCommerce problem
Nothing above is about the plugin. In the mobile run, the only file loaded from the WooCommerce plugin directory under its own URL is a 1,263-byte cookie helper; the weight sits in the site’s own theme (23 requests, 928,400 bytes, fonts included) and in third-party tags. If your question is whether the plugin’s own assets slow a store’s pages down, that is answered separately in our guide to unloading WooCommerce assets.
What this page shows is narrower: a team that knows the techniques, applied them to the font and the hero image, and stopped one layer short on the icon stylesheet and the italic face. Those are both decisions any WordPress theme makes, with or without a store attached.
Fix it yourself
None of this needs us. It needs someone to do it, test that nothing broke, and do it again after the next template change — which is the part we sell.
- Subset the icon stylesheet, not just the icon font The font behind Font Awesome is already split so that a phone fetches only the 15 KB "core" file. The stylesheet that declares it is not: all.min.css is 168,695 bytes (43,967 transferred), sits first in the head, blocks rendering, and defines thousands of icons for a homepage whose markup uses six: chevron-right, arrow-left, arrow-right, circle-check, life-ring and lock. Six inline SVGs, or a stylesheet generated for those six, would take the largest stylesheet on the page off the critical path.
- Do not preload a font that is declared with unicode-range Splitting a font by unicode-range only saves bytes if the browser is left to decide which pieces the page needs. A preload overrides that decision: the file is fetched whether a single glyph from it is drawn or not. We learned this one on this very page (see the limits below), and it applies to any hand-written preload list.
- Decide whether one italic word is worth 245 KB The theme declares a separate variable font for italic Inter, 245,461 bytes as transferred. In the homepage markup, the only italic text is the word "your" inside <em>, in the hero caption "Sell and scale your way". If that face is not declared for the page, the browser synthesises an oblique from the roman file it already has; whether that looks good enough is a design call, but it is one worth making on purpose.
- Fetch slides two to six when the slider needs them The hero slider rotates every five seconds and shows one image at a time, but all six slide images are requested up front: 188,998 bytes on the mobile run. The first slide is correctly marked as high priority. The other five compete with it for the same connection during the seconds that decide the LCP.
- Move jQuery out of the head In the copy of the page we fetched on the day of measurement, jQuery 3.7.1 loads synchronously in the head, ahead of everything below it. The July copy of the same page did not have it there, so this is a recent change. Our study of WooCommerce stores (linked below) looks at how common this is across the platform.
- Count the tag loaders The mobile run downloaded gtm.js and two separate gtag/js files: 545,415 bytes of tag loaders, more than every stylesheet and the HTML document combined. Two gtag/js files usually means two properties configured in two places; consolidating them is a tag-manager setting, not a code change.
What we measured, and what got worse
PageSpeed Insights (Lighthouse, mobile and desktop) run against the live site
and against the same page served through the WebSpeed proxy, on
5 Oct 2026. Audit 2aef2ff74a9aa8 — the numbers
on this page are frozen at that run and are not restated later.
We re-ran the same audit on 25 Oct 2026 and got 53 → 70 on mobile, 68 → 94 on desktop. The teardown below is not rewritten around those numbers: it describes the page as it was measured, and the figures in the text belong to that run. On a phone this page paints its first frame in one of three bands from run to run, so a single pair of scores can move with nothing changed. We have not separated that from what changed on the site and in our proxy over these three weeks. The one set of figures not from the 5 October run is dated in the text: the medians of 19 October (44.5 → 59) come from interleaved PageSpeed runs, outside this audit.
Proxy response re-checked on 2 Nov 2026: the proxied URL returns the page itself, not a redirect back to the origin. That check exists because a redirect makes both halves of a before/after the same page, and the improvement pure noise.
What the page still loaded
The accelerated run made 101 requests against the original's 138, and transferred 4.4 MB against 5.1 MB. We print this because a score can rise for two very different reasons — the page got faster, or the page got less of itself — and across our audit base the second is common enough to be worth ruling out in the open.
What got worse or stayed the same
- On a phone, Lighthouse's LCP for this page depends mostly on when the first frame happens to be painted, and that moment is not stable from run to run. In the observed traces the first paint lands in one of three bands, about 0.25, 1.3 or 2.3 seconds, and the screen is blank until then. This happens on the untouched original and on our version with every optimisation switched off. A run in the early band ends with an LCP of 6 to 13 seconds, a run in a later band with 16 to 24. The audit on 5 October compared an early-band run of the original (LCP 11.4 s) with a late-band run of our version (16.9 s), and that is why paint looked slower. On 19 October we re-measured with interleaved PageSpeed runs: 14 of the original and 15 of our version (a sixteenth was discarded because five requests to our proxy failed during it). Medians: score 44.5 → 59, first contentful paint 5.9 → 4.7 s, speed index 7.2 s on both, largest contentful paint 20.0 → 16.1 s, total blocking time 545 → 96 ms. Within the same band, our version's LCP was lower than the original's in every run. We have not found what decides the band. At these values LCP adds almost nothing to the score either way: past about 8 seconds it scores close to zero. These runs were made directly with PageSpeed Insights and did not pass through the validity checks of an audit, which is why the scores at the top of this page are still the audit's own 51 → 57.
- The first run on 5 October found one cause, and we fixed it the same day. Our version preloaded the "extra" and "brands" icon font files, 449,532 bytes that the original never downloads on a phone because the unicode-range split correctly leaves them out. After the fix our version fetches the same four font files as the original, 794 KB, and the audit scores on this page are from that re-measurement. The fix did not move the paint: in the unthrottled trace, the delay between the hero image arriving and being painted is 200 ms on the original and 2,103 ms on our version, the same as before the fix (2,096 ms). Fonts were not what held it back. The 2.1-second gap is the late paint band described above, and on 19 October the original landed in a late band too, in 12 of 14 runs.
- The desktop result, 74 → 96, is a real improvement in the run, paint included: first contentful paint 1.1 → 0.6 s, largest contentful paint 2.4 → 1.2 s, total blocking time 211 → 38 ms. Two cautions. The original's three desktop runs scored 65, 74 and 95, so the page's own spread is wide. And on desktop the original fetched a second icon font file at 0.85 s, for a glyph drawn after the page loaded; our version did not fetch it during the run, so something the original drew by then was not drawn in ours. We have not identified which element it is. Part of the scripting gain is also that analytics did not run before interaction in our run; a real visitor who clicks still pays for it, later.
- The markup quoted in this teardown was read from a copy of the page fetched on the day of measurement, because the page changes daily (its WooCommerce build is a dated development snapshot). Resource sizes and timings come from the audit itself. On 22 September the same audit landed in review, with our version slower on both devices. One page, one day; nothing here generalises to WooCommerce stores as a class.