Performance

    How to Fix Core Web Vitals in WordPress (LCP, CLS and INP)

    Practical WordPress fixes for LCP, CLS and INP — image and font handling, caching layers, script control, layout stability and the plugin-level causes behind poor field data.

    Published July 30, 202615 min readBy Mazam Khalid
    On this page

    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.

    Diagnose with field data first. Open the Core Web Vitals report in Search Console, note which metric fails and on which URL group, then use PageSpeed Insights on a representative URL from that group. Optimising a page type that already passes changes nothing.

    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-adjust or @font-face descriptors 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.
    Reproduce CLS properly. Test on a throttled mobile connection with the cache disabled. On a fast desktop connection everything loads before paint and the shift never appears.

    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

    1. 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 );
    1. Delay third-party scripts until interaction. Load chat widgets and heatmaps on first scroll or click rather than at page load.
    2. Break up long tasks. Any task over 50ms blocks response; yield with scheduler.yield() or defer non-urgent work.
    3. 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.
    4. Reduce DOM size. Deeply nested builder markup makes every style recalculation expensive.

    A working order of operations

    1. Identify the failing metric and URL group from field data.
    2. Fix hosting and caching so TTFB is consistently low.
    3. Fix the LCP image: format, size, eager loading, preload, priority.
    4. Handle fonts and critical CSS.
    5. Reserve space for every late-arriving element.
    6. Cut and defer JavaScript, starting with third-party tags.
    7. Re-test in the lab, then wait for field data to catch up.
    Change one layer at a time. Enabling six optimisation plugin toggles at once regularly breaks layout or checkout, and you will not know which one did it. Test on staging, then deploy incrementally.

    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.

    Put this into practice

    Move from diagnosis to implementation with a focused engagement for your stack.

    WordPress SEO

    Frequently asked questions

    Why does PageSpeed Insights show 100 while Search Console says my URLs fail?
    PageSpeed lab scores come from a single simulated load. Search Console reports field data from real Chrome users on real devices and networks, aggregated over 28 days. Field data is what counts, and it always lags your fixes by weeks.
    Will a caching plugin fix Core Web Vitals on its own?
    It reliably improves TTFB and therefore LCP. It does very little for INP, which is driven by JavaScript execution, or for CLS, which is a layout problem. Those need theme and script work.
    How long before improvements appear in Search Console?
    The report uses a 28-day rolling window, so meaningful movement typically appears after two to four weeks and a full reflection after roughly a month.

    Field data still failing after the plugin?

    Caching plugins fix the easy 30%. The rest is theme code, script governance and server tuning. I work through the real render path and get the field metrics into the green.

    Mazam Khalid — Technical SEO & AI Search Specialist

    Mazam Khalid works across technical SEO, Google Search Console, indexing and crawl optimization, AI Search Optimization, GEO/AEO, structured data, Core Web Vitals, WordPress, Shopify, Laravel, React and website performance. He also works on Google Ads, Meta Ads, analytics and conversion tracking where technical implementation affects search visibility and lead generation.

    Chat on WhatsApp