LCP WordPress 6.x

Fix LCP in WordPress

Largest Contentful Paint (LCP) measures the time from navigation start until the largest visible content element — typically a hero image, featured image, or large heading — is fully painted on screen. WordPress sites are uniquely challenged because every page request executes PHP, queries a database, loads dozens of plugins, and often serves uncompressed images from a shared server. The result is that the median unoptimized WordPress site has an LCP of 5-8 seconds, far above Google's Good threshold of 2.5 seconds. The five fixes below address the highest-impact causes in order of effort-to-impact ratio: image format optimization, hero image prioritization, render-blocking resource elimination, theme overhead reduction, and server-side caching. Applied together, they consistently bring WordPress LCP under 2 seconds even on modest hosting.

Expected results

Before

5.2s

LCP (Poor) -- unoptimized images, render-blocking scripts, no page cache

After

1.8s

LCP (Good) -- WebP images, prioritized hero, caching, lightweight theme

Step-by-step fix

Serve WebP images with ShortPixel or Imagify

WordPress historically uploads and serves JPEG and PNG images without compression or format conversion. A JPEG hero image that is 400KB could be served as a WebP at 90KB with identical visual quality, finishing download 4x faster on slow connections. Install ShortPixel Image Optimizer or Imagify from the WordPress plugin directory, then configure automatic conversion. Both plugins serve WebP via the <picture> element with a JPEG fallback for legacy browsers, and can retroactively optimize your existing media library.

PHP -- functions.php: WebP delivery via picture element
<?php
// functions.php
// ShortPixel and Imagify handle WebP automatically via their plugins.
// For manual WebP delivery using the native picture element in theme templates:

function mytheme_hero_picture( $attachment_id, $size = 'full' ) {
    $webp_url = str_replace(
        array('.jpg', '.jpeg', '.png'),
        '.webp',
        wp_get_attachment_image_url( $attachment_id, $size )
    );
    $fallback_url = wp_get_attachment_image_url( $attachment_id, $size );
    $attrs        = wp_get_attachment_image_src( $attachment_id, $size );

    printf(
        '<picture>
  <source srcset="%s" type="image/webp">
  <img src="%s" width="%d" height="%d"
       fetchpriority="high" loading="eager" alt="">
</picture>',
        esc_url( $webp_url ),
        esc_url( $fallback_url ),
        (int) $attrs[1],
        (int) $attrs[2]
    );
}

// In your theme template:
// mytheme_hero_picture( get_post_thumbnail_id() );

// ShortPixel Media Settings: Settings > ShortPixel
// [x] Create WebP versions of images
// [x] Deliver WebP in front-end using <picture> tags
// Compression Type: Glossy (recommended for hero images)

Add fetchpriority="high" to the hero image

WordPress 5.5 introduced native lazy loading by adding loading="lazy" to all images rendered through wp_get_attachment_image(). This is correct behavior for below-the-fold images but actively harms LCP when applied to the hero or featured image, because the browser deliberately delays fetching lazy images until they scroll near the viewport. Additionally, adding fetchpriority="high" to the LCP image signals to the browser's preload scanner that this image should be fetched before lower-priority resources. Use the wp_get_attachment_image_attributes filter to modify these attributes based on image context.

PHP -- functions.php: fetchpriority filter
<?php
// functions.php

// Approach 1: filter attributes for the post thumbnail (featured image)
add_filter( 'wp_get_attachment_image_attributes', function( $attr, $attachment, $size ) {
    // Only modify the featured image in singular post/page context
    if ( is_singular() && $attachment->ID === get_post_thumbnail_id() ) {
        $attr['fetchpriority'] = 'high';
        $attr['loading']       = 'eager'; // remove lazy loading
        $attr['decoding']      = 'async';
    }
    return $attr;
}, 10, 3 );

// Approach 2: add rel=preload for the hero image in <head>
add_action( 'wp_head', function() {
    if ( ! is_singular() ) return;
    $thumbnail_id  = get_post_thumbnail_id();
    if ( ! $thumbnail_id ) return;
    $image_url = wp_get_attachment_image_url( $thumbnail_id, 'large' );
    if ( $image_url ) {
        printf(
            '<link rel="preload" as="image" href="%s" fetchpriority="high">' . PHP_EOL,
            esc_url( $image_url )
        );
    }
}, 1 ); // Priority 1 = output as early as possible in <head>

Eliminate render-blocking CSS and JS

Every <script> tag without defer or async, and every <link rel="stylesheet"> in the <head>, blocks HTML parsing until the browser has downloaded and processed that resource. A typical WordPress site with contact form, slider, SEO, and analytics plugins can accumulate 10-15 render-blocking resources totaling several seconds of delay before the browser even begins painting. WP Rocket's File Optimization settings automate deferral; for manual control, WordPress 6.3+ added a strategy argument to wp_enqueue_script().

PHP -- functions.php: defer non-critical scripts
<?php
// functions.php

// WordPress 6.3+: use strategy parameter for defer/async
add_action( 'wp_enqueue_scripts', function() {
    // Defer non-critical scripts
    wp_enqueue_script(
        'my-slider',
        get_theme_file_uri( 'js/slider.js' ),
        array(),
        '1.0.0',
        array(
            'strategy'  => 'defer', // adds defer attribute
            'in_footer' => true,    // move to footer
        )
    );

    // Load analytics asynchronously
    wp_enqueue_script(
        'my-analytics',
        'https://analytics.example.com/tracker.js',
        array(),
        null,
        array( 'strategy' => 'async' )
    );
} );

// WP Rocket plugin settings path:
// WP Rocket > File Optimization
// [x] Minify JavaScript files
// [x] Defer JavaScript loading
// [x] Delay JavaScript execution (for unused-on-load scripts)
// Autoptimize: Settings > Autoptimize
// [x] Optimize JavaScript code
// [x] Force JavaScript in <head> to defer (with exclusions for jQuery)

Use a lightweight theme with minimal DOM

The theme determines the baseline DOM complexity for every page on your site. Heavy themes like Avada or The7 generate 2,000-4,000 DOM nodes per page and load 500KB-1MB of CSS that is only partially used. Google's Lighthouse flags DOM trees over 1,500 nodes as a performance issue. Lightweight themes such as GeneratePress, Kadence, or Astra are built specifically for Core Web Vitals compliance and produce under 700 DOM nodes on a typical page. When using block themes in WordPress 6.x, the theme.json file controls which core styles are loaded, enabling surgical reduction of CSS payload.

JSON -- theme.json: disable unused core styles
// theme.json (block theme root)
// Disable unused WordPress core block styles to reduce CSS payload
{
    "$schema": "https://schemas.wp.org/trunk/theme.json",
    "version": 3,
    "settings": {
        "appearanceTools": true,
        "useRootPaddingAwareAlignments": true
    },
    "styles": {
        "spacing": {
            "blockGap": "1.5rem"
        }
    },
    "__experimentalFeatures": {
        "blocks": {
            "core/navigation": {
                "css": false
            },
            "core/gallery": {
                "css": false
            },
            "core/cover": {
                "css": false
            }
        }
    }
}

// To audit unused CSS:
// Chrome DevTools > Coverage tab (Ctrl+Shift+P > Coverage)
// Load the page, then inspect the red segments in loaded stylesheets
// Anything over 70% unused CSS from theme files is a flag to switch themes

Enable server-side caching with WP Super Cache or Redis

Without caching, every WordPress page request executes 50-200 database queries and runs PHP to assemble the HTML response. On shared hosting this can take 800ms-2,000ms just to produce the first byte, before the browser has received anything to parse or render. Full-page caching stores the assembled HTML as a static file and serves it directly, bypassing PHP and the database entirely. Redis object caching persists the results of expensive database queries in memory, accelerating PHP execution for logged-in users and pages that cannot be full-page cached. Both strategies can be active simultaneously for maximum coverage.

PHP -- wp-config.php: enable caching
<?php
// wp-config.php
// Required for WP Super Cache and most full-page cache plugins
define( 'WP_CACHE', true );

// Redis object cache configuration (if your host provides Redis)
// Install the "Redis Object Cache" plugin, then add:
define( 'WP_REDIS_HOST',     '127.0.0.1' );
define( 'WP_REDIS_PORT',     6379 );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_REDIS_TIMEOUT',  1 );

// Optional: persistent connection key (improves performance)
define( 'WP_REDIS_PREFIX', 'mysite_' );

// WP Super Cache: Settings > WP Super Cache
// Caching On (Recommended)
// [x] Use mod_rewrite to serve cache files (fastest method)
// Cache Timeout: 3600 seconds (1 hour) for mostly-static pages

// After activating Redis Object Cache plugin:
// Settings > Redis > Enable Object Cache
// Status should show "Connected" with server info

// Test TTFB improvement:
// curl -I https://yoursite.com | grep -i "x-cache\|x-wp-cache"

Quick checklist

  • Image optimization plugin (ShortPixel or Imagify) installed and configured to serve WebP automatically
  • Hero/featured image has fetchpriority="high" and loading="eager" via wp_get_attachment_image_attributes filter
  • Non-critical scripts deferred via WP Rocket, Autoptimize, or wp_enqueue_script with strategy => defer
  • Lightweight theme active with DOM node count under 1,500 (verify with Lighthouse Performance audit)
  • WP_CACHE defined as true in wp-config.php and full-page caching plugin active
  • TTFB under 600ms verified via Chrome DevTools Network tab (Time to First Byte column)

Frequently asked questions

Google defines a good LCP as 2.5 seconds or under at the 75th percentile of real-user page loads. For WordPress, the practical target is under 2.0s on desktop and under 2.5s on mobile. Unoptimized WordPress sites typically score 5-8 seconds due to shared hosting latency, uncompressed images, and uncached PHP. With full-page caching, WebP images, a CDN, and a lightweight theme, most WordPress sites can comfortably reach the Good threshold.

Yes. Elementor, Divi, and WPBakery each add 200-600KB of CSS and JavaScript that loads before the page can render. They also produce deeply nested DOM structures that increase browser layout time. If you use Elementor, enable its performance settings to load widget CSS on demand, disable features you do not use, and pair it with WP Rocket. The lightest option for WordPress 6.x is full-site editing with blocks and theme.json, which generates cleaner output with minimal overhead.

Hosting is one of the most significant factors for WordPress LCP. Shared hosting with a TTFB over 600ms makes Good LCP nearly unachievable regardless of other optimizations. Managed WordPress hosts such as Kinsta, WP Engine, and Cloudways include server-side caching, PHP 8.x, and CDN integration at the infrastructure level. For budget hosting, pairing it with Cloudflare's free CDN and full-page caching reduces the impact of slow origin servers substantially.

WP Rocket is the most complete option for LCP improvement, combining full-page caching, JavaScript deferral, CSS optimization, and CDN integration in one plugin. For free options, WP Super Cache and W3 Total Cache handle full-page caching. LiteSpeed Cache is excellent if your host runs LiteSpeed. For database-heavy sites where logged-in users cannot receive cached pages, add the Redis Object Cache plugin with a Redis server to accelerate PHP execution independently.

Open Chrome DevTools, go to the Performance tab, record a page load with 4x CPU throttling and Slow 4G network, then find the LCP marker in the timeline. Click it to highlight the element in the DOM inspector. You can also run Lighthouse: the Diagnostics section shows the LCP element by CSS selector. On WordPress sites the LCP element is almost always the featured image in the hero, a background image set via inline CSS, or a large heading above the fold.

Google rates LCP as 'good' when it is under 2.5 seconds at the 75th percentile. For Wordpress applications specifically, aim for under 2.0 seconds. Measure with field data from Chrome User Experience Report (CrUX) through PageSpeed Insights, as lab tests may not reflect real-user experience with third-party scripts and varying network conditions.

Continue learning