Core Web Vitals

Twenty Audits Later: The Ten Fixes That Delivered Most

Across twenty Core Web Vitals engagements, ten fixes accounted for most of the improvement. What they were, how often each applied, and the order to try them.

6 min read
Core Web VitalsPerformanceLessons
1,026 words6 min read

The pattern behind the audits

After twenty or so Core Web Vitals engagements on small and medium business sites, WordPress and Next.js in roughly equal measure, the fix lists started to look the same. Every site was different in detail; the things that moved the numbers were not. Ten fixes, applied where they were relevant, delivered the large majority of the improvement across the set. The long tail of Lighthouse suggestions delivered the rest, at much higher cost per millisecond.

This is that list, with how often each applied and roughly what it moved when it did. The figures are approximate and from throttled-mobile lab measurements confirmed by field data; treat them as the shape of the return, not a promise.

1. Make the LCP image discoverable and prioritised

Applied on: nearly every site with a failing LCP. Moved: LCP by 0.8 to 2.0 seconds.

The hero was a CSS background, or lazy-loaded, or rendered by JavaScript, or all three. Rendering it as a real <img> in the initial HTML with fetchpriority="high" and no loading="lazy" was the single largest LCP change on most sites. Resource load delay in the trace went from over a second to under a hundred milliseconds.

2. Serve phone-sized images

Applied on: most sites. Moved: LCP by 0.3 to 1.0 seconds; total page weight by half or more.

Honest sizes attributes, a srcset with real widths, WebP or AVIF, and a cap on upload dimensions. On sites where the LCP image was already discoverable, this was what remained.

3. Get HTML cached at the edge

Applied on: most WordPress sites; a few Next.js sites with accidentally dynamic routes. Moved: TTFB by 0.3 to 1.5 seconds, and therefore everything after it.

Full-page caching that actually hit: cookie and query-string exclusions fixed, purge rules made targeted, a CDN caching HTML rather than only assets. On Next.js, finding the Date.now() or cookie read that made a marketing page dynamic and moving it behind Suspense.

4. Defer or remove third-party scripts

Applied on: almost every site. Moved: INP by 50 to 200 milliseconds; LCP by 0.2 to 0.5 seconds.

Tag manager after load, chat widget on click, session replay sampled or removed, social embeds replaced with server-rendered content, the heat-mapping tool from an old experiment deleted. On several sites this was the entire INP fix.

Applied on: most sites with one. Moved: CLS by 0.1 to 0.25; INP on the Accept tap by 100 to 400 milliseconds.

Overlay instead of push, markup in the initial HTML, vendor script deferred, and the Accept handler storing consent and painting before loading tags in sequence. One fix, two metrics.

6. Reserve space for embeds

Applied on: about half of sites. Moved: CLS by 0.05 to 0.2.

Review widgets, maps and video with sized containers, and where possible replaced by server-rendered equivalents. On service-business sites the review carousel near the top of the page was the CLS problem by itself.

7. Self-host fonts with metric overrides

Applied on: about half of sites. Moved: CLS by 0.02 to 0.1; LCP by 0.1 to 0.3 seconds.

Fonts from the site's own origin, preloaded, with size-adjust and related descriptors on the fallback so the swap does not reflow. Framework font loaders do this automatically, which is why it applied mostly to WordPress and older sites.

8. Reduce shipped JavaScript

Applied on: WordPress sites with plugin sprawl; Next.js sites with client-heavy trees. Moved: INP by 50 to 150 milliseconds; LCP by 0.1 to 0.4 seconds.

Dequeue per page and remove unused plugins on WordPress; push 'use client' to the leaves and split heavy islands on Next.js. Slower to do than the fixes above and worth it where INP was still failing after third parties were handled.

9. Persistent object cache and database hygiene

Applied on: WordPress sites, especially stores. Moved: TTFB on uncached pages by 0.3 to 1.0 seconds; admin speed dramatically.

Redis or Memcached, autoloaded options trimmed, transients cleaned, server cron replacing page-load cron. The fix for checkout and account pages that no page cache can help.

10. Break up long tasks around interactions

Applied on: a minority of sites, mostly with search, filters or configurators. Moved: INP by 100 to 300 milliseconds on the specific interaction.

Immediate visual feedback, then the heavy work in a transition or after a yield; debounced inputs; virtualised lists. The most engineering-heavy item, and the one where field attribution was essential to know which interaction to target.

What did not make the list

Minification (already done by every build and most hosts). Removing unused CSS (real but small). Preconnect hints beyond one or two (diminishing). Server-side compression (a flag, already on). "Eliminate render-blocking resources" as a general activity, as opposed to the specific font and third-party fixes above. The Lighthouse score itself, which we stopped reporting.

The order

If you are starting an audit and have a day: 1, 2, 4, 5, in that order, measured after each. They apply to almost every site and together they typically move a Poor site to Needs improvement or Good on LCP and CLS and take a large bite out of INP. Then 3 or 9 depending on the stack, then 6 and 7, then 8 and 10 if INP is still failing and field attribution has named the interaction.

The number in the title

Across the set, sites that completed the relevant items from this list saw their mobile Core Web Vitals field data move from mostly Poor or Needs improvement to mostly Good, and the sites that tracked enquiries alongside saw conversion from mobile search rise by a material fraction, in the region of a quarter to a third, once the data settled. That figure is the one that gets the work approved, and it came from the ten fixes above far more than from anything in the long tail.

This list is the backbone of every Core Web Vitals engagement we run now: field data first to see which apply, traces to confirm, and then the ten in order until the numbers are where they need to be.

All writingHire me for this