Detailed guide

Lcp Optimization Guide

View contents

Complete LCP Optimization Guide: Advanced Techniques for Faster Load Times

Introduction

Largest Contentful Paint (LCP) measures when the largest content element becomes visible to users. While achieving a good LCP score (under 2.5 seconds) might seem straightforward, optimizing LCP requires understanding the underlying factors and applying targeted techniques.

This guide covers advanced optimization strategies based on Google’s official recommendations and real-world performance data from millions of websites.

Understanding LCP Sub-Parts

According to web.dev documentation, LCP can be broken down into four distinct phases. Understanding these phases is crucial for targeted optimization:

The LCP Timeline

Page Load Start → TTFB → Resource Load Delay → Resource Load Time → Element Render Delay → LCP

Optimal budget for 2.5s target:
├── TTFB:                 ~800ms (40%)
├── Resource Load Delay:  ~100ms (less is better)
├── Resource Load Time:   ~800ms (40%)
└── Element Render Delay: ~100ms (less is better)

Phase 1: Time to First Byte (TTFB)

The time from navigation start until the browser receives the first byte of HTML. Target: Under 800ms.

Phase 2: Resource Load Delay

The time between TTFB and when the browser starts loading the LCP resource. Target: Minimize to near zero.

Phase 3: Resource Load Time

How long it takes to load the LCP resource itself (typically an image). Target: Under 800ms.

Phase 4: Element Render Delay

The time from when the resource loads until it’s rendered on screen. Target: Minimize to near zero.

Server-Side Optimizations

1. Reduce TTFB

TTFB is the foundation of good LCP. Every millisecond saved here flows through to LCP improvement.

Use a CDN for static assets:

Without CDN:
User (Chile) → Server (US) = 200ms latency × 3 round trips = 600ms TTFB

With CDN:
User (Chile) → CDN Edge (Chile) = 20ms latency × 3 round trips = 60ms TTFB

Enable server-side caching:

# Nginx - Cache static assets
location ~* \.(jpg|jpeg|png|webp|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# Cache HTML for static pages
location / {
    add_header Cache-Control "public, max-age=3600, stale-while-revalidate=86400";
}

Use HTTP/2 or HTTP/3:

# Nginx HTTP/2 configuration
server {
    listen 443 ssl http2;
    # HTTP/3 (QUIC) if supported
    listen 443 quic;
    add_header Alt-Svc 'h3=":443"; ma=86400';
}

2. Implement Stale-While-Revalidate

Allow serving cached content while fetching fresh content in the background:

Cache-Control: public, max-age=60, stale-while-revalidate=3600

This means:

  • Serve cached content for 60 seconds
  • After 60 seconds, serve stale content while revalidating in background
  • Content is considered stale but usable for up to 1 hour

Eliminate Resource Load Delay

The goal is to make the browser discover and start loading the LCP resource as early as possible.

1. Preload LCP Images

For images that aren’t discoverable in the initial HTML:

<head>
    <!-- Preload with high fetchpriority -->
    <link rel="preload"
          as="image"
          href="/images/hero.webp"
          fetchpriority="high">

    <!-- For responsive images -->
    <link rel="preload"
          as="image"
          href="/images/hero.webp"
          imagesrcset="/images/hero-400.webp 400w,
                       /images/hero-800.webp 800w,
                       /images/hero-1200.webp 1200w"
          imagesizes="100vw">
</head>

2. Inline Critical CSS

CSS blocks rendering. Inline the CSS needed for above-the-fold content:

<head>
    <!-- Inline critical CSS -->
    <style>
        /* Only styles needed for above-the-fold content */
        .hero {
            position: relative;
            min-height: 500px;
        }
        .hero-image {
            width: 100%;
            height: auto;
        }
    </style>

    <!-- Load full stylesheet asynchronously -->
    <link rel="preload" href="/styles/main.css" as="style"
          onload="this.onload=null;this.rel='stylesheet'">
    <noscript>
        <link rel="stylesheet" href="/styles/main.css">
    </noscript>
</head>

3. Avoid Lazy Loading LCP Elements

According to HTTP Archive data, lazy loading the LCP image is one of the most common LCP mistakes:

<!-- ❌ Common mistake: lazy loading hero image -->
<img src="hero.webp" loading="lazy" alt="Hero">

<!-- ✅ Correct: eager loading with high priority -->
<img src="hero.webp"
     loading="eager"
     fetchpriority="high"
     decoding="async"
     alt="Hero">

4. Remove Render-Blocking JavaScript

Move non-critical scripts to the end or use async/defer:

<!-- ❌ Blocks rendering -->
<script src="analytics.js"></script>

<!-- ✅ Doesn't block rendering -->
<script src="analytics.js" defer></script>

<!-- ✅ Loads in parallel, executes when ready -->
<script src="analytics.js" async></script>

When to use each:

  • defer: Scripts that need DOM ready, execute in order
  • async: Independent scripts like analytics, execute immediately when loaded

Optimize Resource Load Time

1. Use Modern Image Formats

WebP and AVIF provide significant size reductions:

<picture>
    <!-- AVIF: Best compression, limited support -->
    <source type="image/avif" srcset="hero.avif">
    <!-- WebP: Good compression, wide support -->
    <source type="image/webp" srcset="hero.webp">
    <!-- JPEG: Fallback for older browsers -->
    <img src="hero.jpg" alt="Hero image">
</picture>

Typical savings:

Original JPEG:     500KB (baseline)
WebP:              300KB (-40%)
AVIF:              200KB (-60%)

2. Implement Responsive Images

Serve appropriately sized images for each viewport:

<img src="hero-800.webp"
     srcset="hero-400.webp 400w,
             hero-800.webp 800w,
             hero-1200.webp 1200w,
             hero-1600.webp 1600w"
     sizes="(max-width: 600px) 100vw,
            (max-width: 1200px) 80vw,
            1200px"
     alt="Hero image"
     width="1200"
     height="600"
     fetchpriority="high">

3. Use Image CDN for Dynamic Optimization

Services like Cloudflare Images, imgix, or Cloudinary can optimize images on-the-fly:

<!-- Cloudflare Image Resizing -->
<img src="/cdn-cgi/image/width=800,format=auto,quality=80/hero.jpg"
     alt="Hero">

<!-- Dynamic sizing based on viewport -->
<img src="/cdn-cgi/image/width=auto,format=auto/hero.jpg"
     sizes="100vw"
     alt="Hero">

4. Optimize Font Loading for Text LCP

If your LCP element is text, font loading can delay it:

<head>
    <!-- Preload critical font -->
    <link rel="preload"
          href="/fonts/main.woff2"
          as="font"
          type="font/woff2"
          crossorigin>

    <!-- Use font-display: swap -->
    <style>
        @font-face {
            font-family: 'MainFont';
            src: url('/fonts/main.woff2') format('woff2');
            font-display: swap;
        }
    </style>
</head>

Minimize Element Render Delay

1. Avoid CSS That Hides LCP Elements

CSS animations or initial hidden states delay LCP:

/* ❌ Delays LCP - element starts hidden */
.hero-image {
    opacity: 0;
    animation: fadeIn 0.5s forwards;
}

/* ✅ Better - element visible immediately */
.hero-image {
    opacity: 1;
}

2. Reduce JavaScript That Modifies LCP Element

Client-side rendering that builds the LCP element delays LCP:

// ❌ LCP delayed until JS executes
document.getElementById('hero').innerHTML = '<img src="hero.webp">';

// ✅ Better - include LCP element in HTML
// Let the browser handle it natively

3. Use Content-Visibility for Below-Fold Content

Prevent below-fold content from affecting LCP rendering:

/* Skip rendering of off-screen content */
.below-fold-section {
    content-visibility: auto;
    contain-intrinsic-size: 0 500px;
}

Advanced Optimization Techniques

1. Priority Hints

Use the fetchpriority attribute to signal resource importance:

<!-- High priority for LCP image -->
<img src="hero.webp" fetchpriority="high" alt="Hero">

<!-- Low priority for below-fold images -->
<img src="footer-logo.webp" fetchpriority="low" alt="Logo">

<!-- High priority for critical script -->
<script src="critical.js" fetchpriority="high"></script>

2. Early Hints (103 Status Code)

Send hints about critical resources before the main response:

HTTP/1.1 103 Early Hints
Link: </styles/critical.css>; rel=preload; as=style
Link: </images/hero.webp>; rel=preload; as=image

HTTP/1.1 200 OK
Content-Type: text/html
...

3. Speculative Loading

Prerender pages users are likely to visit:

<!-- Speculation Rules API -->
<script type="speculationrules">
{
    "prerender": [
        {
            "where": { "href_matches": "/products/*" },
            "eagerness": "moderate"
        }
    ]
}
</script>

Measuring and Monitoring LCP

Lab Testing

# Lighthouse CLI
lighthouse https://example.com --only-categories=performance

# WebPageTest
webpagetest test https://example.com --location=ec2-sa-east-1

Field Data (RUM)

// Web Vitals library
import {onLCP} from 'web-vitals';

onLCP(({value, element}) => {
    console.log('LCP:', value, 'Element:', element);
    // Send to analytics
    analytics.track('LCP', {
        value,
        element: element?.tagName,
        url: window.location.href
    });
});

Chrome DevTools

  1. Open DevTools → Performance tab
  2. Check “Web Vitals” checkbox
  3. Record a page load
  4. Look for the “LCP” marker in the timeline
  5. Hover to see which element triggered LCP

Common LCP Optimization Patterns

Pattern 1: Hero Image Page

<!DOCTYPE html>
<html>
<head>
    <link rel="preload" as="image" href="hero.webp" fetchpriority="high">
    <style>/* Inline critical CSS */</style>
</head>
<body>
    <img src="hero.webp"
         fetchpriority="high"
         loading="eager"
         width="1200" height="600"
         alt="Hero">
</body>
</html>

Pattern 2: Text-Heavy Page (Blog)

<!DOCTYPE html>
<html>
<head>
    <link rel="preload" href="font.woff2" as="font" crossorigin>
    <style>
        @font-face {
            font-family: 'Article';
            src: url('font.woff2') format('woff2');
            font-display: swap;
        }
        h1 { font-family: 'Article', serif; }
    </style>
</head>
<body>
    <h1>Article Title That Is the LCP Element</h1>
</body>
</html>

Checklist: LCP Optimization

Server & Network
├── [ ] TTFB under 800ms
├── [ ] CDN configured for static assets
├── [ ] HTTP/2 or HTTP/3 enabled
├── [ ] Compression (Brotli/Gzip) enabled
└── [ ] Caching headers configured

Resource Discovery
├── [ ] LCP image preloaded
├── [ ] Critical CSS inlined
├── [ ] Render-blocking JS eliminated
└── [ ] fetchpriority="high" on LCP element

Resource Optimization
├── [ ] Modern image formats (WebP/AVIF)
├── [ ] Responsive images with srcset
├── [ ] Image dimensions specified
├── [ ] Fonts preloaded with font-display: swap
└── [ ] LCP image NOT lazy loaded

Render Optimization
├── [ ] No CSS hiding LCP element
├── [ ] No JS building LCP element
├── [ ] content-visibility for below-fold
└── [ ] Third-party scripts deferred

References

This article was enhanced using authoritative sources:

[1] web.dev: Optimize Largest Contentful Paint https://web.dev/articles/optimize-lcp Google’s comprehensive guide to LCP optimization covering all four sub-parts: TTFB, resource load delay, resource load time, and element render delay. Includes specific techniques and time budgets for achieving good LCP scores.

[2] web.dev: LCP Breakdown https://web.dev/articles/lcp#what-is-a-good-lcp-score Detailed breakdown of LCP phases with recommended time allocations. Explains why 40% of the 2.5s budget should go to TTFB and resource load time each, with minimal time for delays.

[3] Chrome Developers: Resource Loading Optimization https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preload Technical documentation on preload hints, fetchpriority attribute, and Priority Hints API for optimizing critical resource loading that affects LCP.

[4] HTTP Archive Web Almanac: Performance https://almanac.httparchive.org/en/2024/performance Industry-wide performance data showing common LCP bottlenecks across millions of websites, including adoption rates of optimization techniques and real-world improvement patterns.


Note: This article is part of our SEO analysis series. Explore all articles in the Performance SEO Hub.


Sources: web.dev (LCP Optimization), Chrome Developers, HTTP Archive Web Almanac

Related articles

Related version

Introduction

Lcp Explained

Largest Contentful Paint (LCP) is one of Google's Core Web Vitals—a set of metrics that measure real-world user experience on your website

Category hub

Hub

Performance Seo Hub

Performance is a critical ranking factor and directly impacts user experience

In the same category

Detailed guide

Fcp Optimization Guide

First Contentful Paint (FCP) measures when users first see content on your page

Detailed guide

Ttfb Optimization Guide

Time to First Byte (TTFB) is the foundation of web performance—every millisecond of TTFB delays your entire page load