Core Web Vitals fail on WordPress for a small set of recurring reasons: an unoptimised hero image, render-blocking theme and plugin assets, fonts that swap late, third-party scripts monopolising the main thread, and dynamic content injected after paint. Fix those in the right order and most sites pass without exotic tooling.
LCP: Largest Contentful Paint
Target: under 2.5s at the 75th percentile. On WordPress the LCP element is almost always the hero image or the first heading block. LCP breaks into four parts, and you need to know which one dominates.
1. Time to first byte
- Enable full-page caching (server-level where your host supports it, otherwise a caching plugin).
- Add object caching with Redis or Memcached — this is what removes repeated slow queries on sites with heavy plugin stacks.
- Run current PHP with OPcache enabled.
- Put a CDN in front so cached HTML and assets are served from an edge close to the user.
If TTFB alone exceeds a second on a cached page, no front-end work will rescue LCP. Fix the server layer first.
2. Resource load delay
The browser must discover the LCP image before it can request it. Two common WordPress mistakes:
<!-- Wrong: hero image lazy-loaded, discovered late -->
<img src="hero.jpg" loading="lazy" alt="...">
<!-- Right: eager, high priority, preloaded -->
<link rel="preload" as="image" href="hero.avif" fetchpriority="high">
<img src="hero.avif" loading="eager" fetchpriority="high"
width="1600" height="900" alt="Descriptive alt text">
Many optimisation plugins lazy-load every image including the hero. Most offer an "exclude above-the-fold images" setting — use it, and verify in DevTools that the LCP element is not lazy.
3. Resource load time
- Serve AVIF or WebP with correct dimensions per breakpoint via
srcset. - Compress to a sensible quality; a 1.8MB hero JPEG is the most common single cause of poor LCP.
- Avoid background images set in CSS for the hero — they are discovered only after the stylesheet parses.
4. Element render delay
- Inline critical CSS for above-the-fold layout and defer the rest.
- Self-host fonts, preload the primary weight, and use
font-display: swap. - Limit font families and weights — every extra weight is another blocking request.
- Move non-essential scripts out of
<head>.
CLS: Cumulative Layout Shift
Target: under 0.1. CLS is a discipline problem, not an optimisation problem — anything inserted after paint without reserved space costs you.
- Always set width and height on images and iframes, including within post content.
- Reserve space for ads and embeds with a fixed-height container matching the expected slot.
- Cookie banners and promotion bars: overlay them rather than pushing content down, or reserve the bar height in the layout.
- Fonts: match fallback metrics with
size-adjustor@font-facedescriptors so the swap does not reflow text. - Sliders and carousels: give the container a fixed aspect ratio so the first slide does not resize the page.
- Dynamic notices (stock warnings, cart messages) rendered by plugins after load — reserve their space or render them server-side.
INP: Interaction to Next Paint
Target: under 200ms. INP replaced FID and is far harder to fake, because it measures the responsiveness of real interactions across the whole session. On WordPress the causes are consistent:
- jQuery-dependent plugin stacks. Five plugins each loading their own scripts on every page, whether used or not.
- Third-party tags. Analytics, heatmaps, chat widgets, A/B testing and pixels all compete for the main thread.
- Heavy page builders. Builder runtimes execute layout logic client-side on every page load.
- Unoptimised event handlers doing synchronous work on scroll, input or click.
Fixes that actually move INP
- Dequeue what is not needed. Conditionally unload plugin assets on pages where the plugin is not used:
add_action( 'wp_enqueue_scripts', function () {
if ( ! is_page( 'contact' ) ) {
wp_dequeue_script( 'contact-form-plugin' );
wp_dequeue_style( 'contact-form-plugin-css' );
}
}, 100 );
- Delay third-party scripts until interaction. Load chat widgets and heatmaps on first scroll or click rather than at page load.
- Break up long tasks. Any task over 50ms blocks response; yield with
scheduler.yield()or defer non-urgent work. - Audit the builder. If a page builder's runtime is the bottleneck on key templates, rebuilding those templates in native blocks or theme code is often the only durable fix.
- Reduce DOM size. Deeply nested builder markup makes every style recalculation expensive.
A working order of operations
- Identify the failing metric and URL group from field data.
- Fix hosting and caching so TTFB is consistently low.
- Fix the LCP image: format, size, eager loading, preload, priority.
- Handle fonts and critical CSS.
- Reserve space for every late-arriving element.
- Cut and defer JavaScript, starting with third-party tags.
- Re-test in the lab, then wait for field data to catch up.
Verification
After deployment, confirm with the Chrome UX Report data in PageSpeed Insights rather than the lab score, watch the Search Console Core Web Vitals report over the next month, and keep an eye on conversion and bounce behaviour — performance work that breaks a form is a net loss regardless of the score.