Skip to main content
ModPageSpeed 2.0: AVIF, WebP, and critical CSS — up to 69% less page weight on the live demo

How to fix LCP (Largest Contentful Paint)

Largest Contentful Paint (LCP) measures how long the largest element in the viewport — usually a hero image, a video poster, a banner, or the first heading — takes to render during load. It is one of the three Core Web Vitals, and Google scores it on field data: the 75th percentile of real Chrome users over the trailing 28 days, from the CrUX dataset. A page is good under 2.5 s and poor over 4 s.

There is no single fix for LCP, because it is not one number — it is four. PageSpeed Insights splits the LCP element into time to first byte, resource load delay, resource load duration, and element render delay. Each phase has different causes and different owners: some live in your backend, some in how the markup is delivered, some in your own application code. This page walks the four phases, then maps fixes to them, including the cases a server-layer optimizer cannot touch. When you are ready, analyze a page to see which phase is the long pole.

LCP thresholds Good: Under 2.5 s (at the 75th percentile) Poor: Over 4 s (at the 75th percentile)

What LCP measures

Largest Contentful Paint is the render time of the largest element visible in the viewport during load — often a hero image, a video poster, a banner, or the first heading (the H1). The field figure comes from Chrome's CrUX dataset (real users over the trailing 28 days) and is what Google ranks on; the lab figure comes from Lighthouse and PageSpeed Insights, a single synthetic test. The two routinely disagree, because real visitors run slower hardware on slower connections than the test machine — optimize toward the field number. To make LCP diagnosable, PSI breaks the LCP element into four phases: TTFB (time to first byte, how long the backend takes to start sending HTML), resource load delay (how long before the browser starts fetching the LCP resource), resource load duration (how long that resource takes to download), and element render delay (how long after the resource arrives before the element paints). Fixing LCP means finding which phase dominates and fixing that one; the others are wasted effort.

A single lab run from PageSpeed Insights approximates LCP; the field number from real Chrome users is what Google ranks on.

Common causes

The LCP image is discovered late (resource load delay)

The hero/LCP image is not preloaded, so the browser does not learn it exists until it has parsed the HTML and, often, the CSS that references it. The fetch starts hundreds of milliseconds late. A <link rel=preload as=image> hint plus fetchpriority=high lets the browser start the download as soon as it sees the HTML.

Render-blocking CSS delays first paint (element render delay)

A chain of render-blocking stylesheets must download and parse before the browser will paint anything, so even an image that arrived on time cannot render until the CSS chain clears. Inlining the critical above-the-fold CSS removes that dependency.

A large unoptimized image takes too long to download (resource load duration)

A multi-megapixel JPEG or PNG served at full size is the LCP resource on most pages. Transcoding it to WebP or AVIF and serving a variant sized to the viewport cuts the bytes, so the same image finishes downloading sooner.

TTFB is the long pole (the backend is slow to first byte)

If the backend is slow to send the first byte, every later phase starts late and LCP cannot be good no matter what you do to the image. No HTML rewriting fixes this — fix the backend first: caching, a faster origin, edge HTML caching. An origin-side optimizer cannot make a slow application respond faster.

nginx transport bottlenecks queue the LCP image

On nginx specifically: on-the-fly gzip instead of pre-compressed assets burns CPU per request; missing HTTP/2 means HTTP/1.1 head-of-line blocking queues the LCP image behind CSS and JS; and a missing Cache-Control: s-maxage leaks HTML onto the slow path instead of serving it from cache. All three inflate either TTFB or load delay.

A text LCP waits on a web font, or the optimizer cannot see the element

When the LCP element is text, a web font loaded with font-display: swap can add render delay. And when the LCP element is a client-rendered SPA node or a third-party embed, an origin-side optimizer cannot see it in the response markup, so it cannot preload or rewrite it — that fix is application-layer.

How to fix LCP

Diagnose first: find the LCP element and its long-pole phase

Server + app

Start in the field, not the lab. Find failing URLs in Search Console (CrUX), then run one through the analyzer or PageSpeed Insights to identify the LCP element and read its four-phase breakdown (TTFB, load delay, load duration, render delay). Reproduce under throttling, fix the phase that dominates, then re-measure in the field. Skipping this step is how teams optimize an image when the real long pole was TTFB.

Preload the LCP image so the browser fetches it early

Server layer

This attacks resource load delay. ModPageSpeed gives the detected LCP candidate image a <link rel=preload as=image> hint plus fetchpriority=high, so the browser starts the fetch earlier instead of discovering the image late — enabled by default. The worker can also send 103 Early Hints with Link: rel=preload headers before the origin responds, plus preconnect for discovered third-party origins. On mod_pagespeed 1.15 (Apache/nginx) the equivalent filters are hint_preload_subresources and inline_preview_images (an LQIP placeholder).

Inline critical CSS to remove the render-blocking chain

Server layer

This attacks element render delay. ModPageSpeed 2.0's worker extracts heuristic critical CSS with no headless browser — it scans the HTML and matches selectors against the DOM (first 25 elements; header/nav/hero patterns; html/body/:root/universal selectors) — then injects the result as a <style> tag before </head>, removing render-blocking stylesheet requests. mod_pagespeed 1.15 does the same via prioritize_critical_css. An optional --enable-browser-analysis pipeline adds coverage validation and async CSS loading.

Shrink the LCP image: WebP/AVIF + viewport-aware variants

Server layer

This attacks resource load duration. ModPageSpeed 2.0 transcodes images to WebP and AVIF based on the client's Accept header and serves viewport-aware responsive variants (mobile/tablet/desktop, 1x/2x), so the hero downloads sooner. A single decode produces up to 37 cache variants, and every variant is verified against the original with SSIMULACRA2 before it is cached. mod_pagespeed 1.15 uses recompress_images + convert_jpeg_to_webp. Optimization is transparent: no URL rewrites, no markup changes, no build-pipeline changes.

Fix TTFB first if the backend is the long pole

Server + app

If the four-phase breakdown shows TTFB dominating, the rewriter cannot help — the backend must be fixed first. On nginx that means serving pre-compressed assets instead of on-the-fly gzip, enabling HTTP/2 so the LCP image is not queued behind CSS/JS by HTTP/1.1 head-of-line blocking, and adding Cache-Control: s-maxage so HTML is cached rather than regenerated per request. ModPageSpeed serves the origin immediately on a cache miss and optimizes the variant out of the request path, but it does not make a slow application respond faster.

For SPA, third-party, and font-driven LCP, fix it in the app

Application

Some LCP causes are out of reach for an origin-side optimizer. ModPageSpeed cannot rewrite third-party iframe contents, does not see the runtime DOM of client-rendered SPAs, and does not manage your font-loading strategy. If your LCP element is rendered client-side, embedded from a third party, or a text block waiting on font-display: swap, the fix lives in your application — server-render the LCP node, preload the font, or move the embed off the critical path.

Fix LCP on your platform

The same metric fails for different reasons on different stacks. Pick yours for the concrete diagnosis and fix.

LCP: common questions

What is a good LCP score?
LCP is good under 2.5 seconds and poor over 4 seconds; the band between 2.5 s and 4 s is 'needs improvement.' The score that counts is measured at the 75th percentile of real users in the field (Chrome's CrUX dataset, trailing 28 days), not a single lab test. Meeting 2.5 s at the 75th percentile means roughly three of every four visits load the largest element quickly.
How do I fix LCP?
Find which of the four phases is the long pole, then fix that one. If the LCP image is discovered late, preload it with <link rel=preload as=image> and fetchpriority=high. If render-blocking CSS is the delay, inline the critical above-the-fold CSS. If the image is too heavy, transcode it to WebP/AVIF and serve a viewport-sized variant. If TTFB dominates, fix the backend first. ModPageSpeed does the first three automatically at the server layer; the TTFB fix and any SPA/font fix are yours.
Why do my lab and field LCP numbers disagree?
Lab data is a single synthetic run from Lighthouse or PageSpeed Insights on a fast test machine; field data comes from real Chrome users on slower hardware and slower connections, aggregated over the trailing 28 days in CrUX. The field number is almost always worse, and it is the one Google ranks on. Optimize toward the field figure, and use the lab breakdown only to diagnose which phase to attack.
What are the four phases of LCP?
PageSpeed Insights splits LCP into TTFB (how long the backend takes to send the first byte), resource load delay (how long before the browser starts fetching the LCP resource), resource load duration (how long that resource takes to download), and element render delay (how long after it arrives before the element paints). Each phase has different causes, so diagnosing the dominant phase tells you which fix will move the number.
Can a server-side optimizer fix LCP?
Yes, for three of the four phases. ModPageSpeed inlines critical CSS to cut render delay, transcodes and right-sizes the hero image to cut load duration, and preloads the detected LCP image to cut load delay — each attacks a distinct part of the timing breakdown, all at the server layer below the CMS. It cannot fix LCP when TTFB is the long pole (fix the backend first), and it cannot see a client-rendered SPA node, a third-party iframe, or your font-loading strategy.
Should I lazy-load the LCP image?
No. The LCP image is, by definition, in the viewport at load, so lazy-loading it delays its discovery and download and makes LCP worse. Lazy-loading belongs on off-screen images. ModPageSpeed applies loading=lazy only to off-screen <img> and <iframe> elements and instead preloads the detected LCP candidate — both enabled by default.

Measure, then fix

Start with a measurement. Run a page through the analyzer to see what is driving its LCP, then fix it at the server layer with ModPageSpeed — it optimizes immediately, licensed or not. Production use requires a commercial license — but the software never locks you out.

See also: