From 6.3 MB to 0.37 MB: Anatomy of a 10x Faster Website

From 6.3 MB to 0.37 MB: Anatomy of a 10x Faster Website — cover illustration

This is a case study we can publish with unusual confidence, because the patient was this very website. In one working session we took our home page from a 6.3 MB payload and a 34.6-second mobile Largest Contentful Paint to 0.37 MB and 3.6 seconds — and accessibility from 76 to 100. Here is exactly what was wrong, what we changed, and what we measured. No screenshots of other people's sites; our own numbers.

The baseline (it was bad)

Metric (mobile)BeforeAfter
Performance score5076 (89 desktop)
Largest Contentful Paint34.6 s3.6 s
Cumulative Layout Shift0.2590
Accessibility84100
Best Practices96100
Home page weight6.3 MB0.37 MB

Post-fix numbers are lab measurements (Lighthouse, simulated 4G) against the production build served locally; production field numbers will vary.

Finding #1: one image was 82% of the page

The hero background was a 5.14 MB, 2816-pixel PNG. Two sibling pages carried 9.4 MB and 6.8 MB PNGs of their own — 21.5 MB of hero images across three pages. The fix is boring and worth more than every other optimization combined: resize to the largest rendered width (1920px) and convert to WebP. Result: 5.14 MB → 191 KB, visually indistinguishable under the hero's dark overlay. Because the image was a CSS background — invisible to the browser's preload scanner until CSS parses — we added <link rel="preload" as="image" fetchpriority="high">.

Finding #2: the server sent everything raw, every time

The web server had no compression and no cache headers: 550 KB of CSS/JS traveled uncompressed on every single visit. Enabling gzip cut the text payload by ~75% (our largest stylesheet went from 89 KB to 14.5 KB on the wire), and cache headers made repeat visits nearly free. One caveat worth stealing: our regex-based cache rules would have silently captured paths that must be proxied to another service — prefix locations with ^~ shields them. We found that in testing, not in production, which is the only acceptable place to find it.

Finding #3: death by a thousand render-blockers

An entire icon font loaded for exactly one glyph (replaced with an inline SVG). Twenty webfont variants declared, six actually used. Three scripts parsed synchronously before first paint (now defer). A decorative full-screen loader held the page hostage for 900 ms after everything had already loaded. And a leftover template widget requested a file deleted from the build — a guaranteed 404 and console error on every page load, which alone had been costing four Best Practices points.

Finding #4: layout shift hides in good intentions

Our biggest CLS lesson: adding width/height attributes to an image using its intrinsic dimensions made things worse, because CSS constrained the rendered height — the header reserved 165px, collapsed to 40px, and shifted everything below it. The attributes must describe the rendered geometry. CLS went from 0.259 to zero.

What we deliberately did not do

No framework migration, no build pipeline, no tree-shaking of the legacy bundle. Discipline matters: 90% of the win came from images, compression and headers — changes with near-zero regression risk. The remaining mobile points live in critical-CSS work whose risk/benefit is a separate, conscious decision.

Key takeaways

  • Weigh your page before optimizing anything: one asset was 82% of ours.
  • gzip + cache headers are a 30-minute change with a permanent payoff.
  • Image width/height attributes must match rendered, not intrinsic, geometry.
  • Measure, change, re-measure — and publish your numbers.

Working in a regulated industry and wrestling with the problems described here? We build software for exactly this.

Talk to PBS