LCP (Largest Contentful Paint) is the Core Web Vital most sites fail first and fix last. Google's threshold is 2.5 seconds on mobile for 75% of visits, and our May 2026 pass-rates study of 13.75 million origins found that LCP is the weakest of the three Core Web Vitals, it is the single metric most responsible for the mobile web only passing 49.1% of the time. The good news is that LCP has only four moving parts, and once you know which of the four is dominant on your site, the fixes are predictable.
This guide walks through diagnosing which sub-part of LCP is slow, then works through the fixes in order of impact. For the underlying definition and thresholds, see the LCP glossary entry - this guide focuses entirely on how to make the number smaller.
TL;DR
LCP time is the sum of four sub-parts: TTFB, resource load delay, resource load time, and render delay. Fix them in that order. Slow TTFB (above ~600 ms) usually means hosting or caching - a good CDN and page cache cut it in half. If the LCP element is an image, make it discoverable in the initial HTML and preload it with fetchpriority=high. Remove render-blocking CSS and JS so the browser can start on the LCP resource. Compress and correctly size the LCP image. Fix font-blocking when the LCP element is text. Never lazy-load the LCP element. Verify in field data over a 28-day CrUX window, not a single Lighthouse run.
Key Takeaways
- ✓LCP breaks into four sub-parts: TTFB, resource load delay, resource load time, and render delay. The right fix depends on which one dominates.
- ✓The biggest wins in order: fix TTFB, make the LCP image discoverable and preloaded, remove render-blocking CSS and JS, compress and correctly size the image.
- ✓Never lazy-load the LCP element and never mark it fetchpriority=low. Both are common accidents that add 800-2000 ms to LCP.
- ✓Preloading the LCP image with fetchpriority=high moves it to the front of the network queue and typically shaves 200-800 ms.
- ✓When the LCP element is text (a heading), font loading is often the bottleneck. font-display: swap and preloading the font file are the fix.
- ✓Verify LCP fixes in CrUX field data after the 28-day window rolls, not with a single Lighthouse run in the browser.
What Counts as the LCP Element
LCP measures the render time of the largest text block or image visible in the viewport during the initial page load. The exact rules (which elements qualify, how size is computed, what counts as "visible") are covered in the Largest Contentful Paint glossary entry.
For the purposes of this guide, all you need to know is that on most sites the LCP element is one of three things: a hero image, a background image on a hero section, or a large heading above the fold. Open your page in Chrome DevTools > Performance, record a load, and the LCP marker on the timeline points at the element. That is the element every fix in this guide is trying to render faster.
The Four Sub-Parts of LCP Time
LCP is a sum, not a single event. Total LCP time equals the sum of four sub-parts, and knowing which one dominates on your site tells you which fix will actually move the number.
1. TTFB (Time to First Byte). How long it takes the server to respond with the first byte of HTML. Bad hosting and no caching typically produce TTFB between 600 ms and 1.5 s. Good origins on a CDN sit under 200 ms.
2. Resource load delay. The gap between the first byte arriving and the browser starting to fetch the LCP resource (usually an image). This is where render-blocking CSS, JavaScript that delays HTML parsing, and LCP images hidden inside JavaScript-driven components live. Common range: 100-1500 ms.
3. Resource load time. How long the LCP resource itself takes to download. Governed by file size, format, and network. A 900 KB hero image on a slow 4G connection is 2+ seconds by itself.
4. Render delay. The gap between the LCP resource finishing download and the browser painting it. Governed by main-thread work: parsing large JavaScript bundles, hydrating a client-side framework, or waiting for a webfont to swap in.
How to diagnose which one dominates. Run PageSpeed Insights on the page and open the LCP breakdown. It reports each sub-part in milliseconds. Whichever is largest is where the work is. If the diagnostics tab shows "Largest Contentful Paint element," open Chrome DevTools > Performance, record a load, and inspect the LCP marker's timing details. The single largest sub-part is your first target.
1. Fix TTFB First
If TTFB is above 600 ms, no amount of front-end optimization will drag LCP under 2.5 s. The server has to respond faster.
Turn on full-page caching. For every URL that does not depend on the logged-in user, the server should return a pre-rendered HTML page from cache in under 100 ms. On WordPress this is a caching plugin; on custom stacks it is a reverse proxy like Varnish or a CDN edge cache.
Put a CDN in front of the origin. Cloudflare, Fastly, and Bunny CDN all cache HTML at the edge (when configured to do so) and cut TTFB for global visitors from 400-800 ms to 40-120 ms. The default WordPress or Shopify setup does not cache HTML at the edge - you have to enable it.
Upgrade to a host that can serve uncached requests fast. Cart, checkout, and logged-in pages have to run through PHP and hit the database. On undersized shared hosting those requests take 800-1500 ms even with everything else optimized. See the fastest WordPress hosting comparison for stacks that handle uncached requests well.
A TTFB under 200 ms is the target. Under 400 ms is acceptable. Above 600 ms should be fixed before anything else on this page.
2. Make the LCP Image Discoverable and Preload It
If the LCP element is an image, the single highest-ROI change after fixing TTFB is making sure the browser knows about that image as early as possible.
The browser's preload scanner reads the HTML as bytes arrive and starts fetching images before the page even parses. For that to work, the LCP image has to be in the initial HTML as a normal `<img>` tag with a resolvable `src` (or a `srcset`). Images inserted by JavaScript after hydration are invisible to the preload scanner and the browser only finds them after parsing and running the script, which routinely adds 500-1500 ms of resource load delay.
Add fetchpriority="high" to the LCP image. Browsers default images to low or medium priority. Setting `fetchpriority="high"` on the LCP `<img>` moves it to the front of the download queue.
<img src="/hero.webp" alt="..." width="1200" height="600" fetchpriority="high">
Preload the LCP image in the head when the image URL is known at build time and the image is not in the initial HTML for a legitimate reason (for example, a picture element with responsive sources selected by media queries).
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high" imagesrcset="..." imagesizes="...">
Do not preload more than one image. Preloading three hero candidates means the browser downloads three when it only needs one, and you have made every non-LCP page slower.
3. Remove Render-Blocking Resources
A render-blocking resource is a CSS file or synchronous script in the `<head>` that the browser must download and process before it can render anything, including the LCP element. The full concept is in the render-blocking resources glossary entry.
For CSS, extract the critical (above-the-fold) CSS and inline it in the head; load the rest of the stylesheet asynchronously. On WordPress this is what WP Rocket, LiteSpeed Cache, and FlyingPress automate. On custom stacks, tools like Critical or Beasties do the extraction.
For JavaScript, add `defer` (or `async`) to every script tag in the head that is not required for above-the-fold rendering. Defer parses the HTML uninterrupted and only runs the script after parsing is done - it is the safe default for almost everything.
For third-party scripts (analytics, chat, tag manager), consider delaying them until user interaction instead of just deferring. A delayed script does not run until the user scrolls, moves the mouse, or touches the screen, which keeps them out of the initial LCP path entirely.
4. Optimize the LCP Image Itself
Once the browser knows about the image and starts downloading it early, the last question is how big that download is.
Serve WebP or AVIF, not JPEG or PNG. WebP is 25-35% smaller than JPEG at the same visual quality. AVIF is another 20-30% smaller than WebP but with worse browser support for older devices.
Size it correctly. A 3000-pixel-wide hero image displayed at 1200 pixels is downloading 6x more pixel data than the browser will use. Use responsive `srcset` and `sizes` so mobile devices get a mobile-sized image.
Compress it. Quality 75-82 for JPEG, 75-80 for WebP, 50-65 for AVIF is usually visually indistinguishable from the original and half the bytes.
Target under 200 KB for the LCP image. Above 500 KB the resource load time alone can exceed the 2.5 s budget on mobile.
For the WordPress-specific pipeline (plugins, WebP conversion, correct srcset generation), see WordPress image optimization.
5. Fix Font Blocking When LCP Is a Heading
When the LCP element is a heading, the browser often has the text ready to paint but is waiting for the webfont file to download before it will render the text. The heading paints late, and LCP is measured at that late paint.
Add `font-display: swap` to your @font-face rules. This tells the browser to paint the text immediately with a fallback font, then swap to the webfont when it arrives. LCP is measured at the fallback paint, which happens hundreds of milliseconds earlier.
@font-face {
font-family: 'Inter';
font-display: swap;
src: url('/inter.woff2') format('woff2');
}
Preload the webfont file used above the fold so it downloads in parallel with the CSS instead of after it.
<link rel="preload" as="font" href="/inter.woff2" type="font/woff2" crossorigin>
Self-host fonts instead of loading from Google Fonts. Self-hosted removes an extra DNS lookup and TCP connection, which typically saves 100-300 ms on the font's arrival.
6. Never Lazy-Load the LCP Element
Native lazy loading (`loading="lazy"` on `<img>`) tells the browser to defer the image download until it is near the viewport. That is exactly the wrong behavior for the LCP element because the LCP element is by definition already in the viewport on load.
A lazy-loaded LCP image typically adds 800-2000 ms because the browser will not start the fetch until layout is complete and the intersection observer confirms the image is visible.
Audit every `<img loading="lazy">` in above-the-fold markup. For the LCP image specifically, set `loading="eager"` explicitly or omit the attribute entirely (eager is the default).
Similarly, do not set `fetchpriority="low"` on the LCP image, do not put it inside a `<picture>` element with `loading="lazy"`, and do not hide it behind a CSS `content-visibility: hidden` above the fold.
Platform Notes
WordPress. The most common LCP wins on WordPress are: turn on a real caching plugin, put Cloudflare in front, add `fetchpriority="high"` to the hero image (WP Rocket and similar plugins do this automatically for the first content image), and convert images to WebP. The full stack is in how to speed up your WordPress site.
Shopify. Shopify's edge cache handles TTFB for you, so LCP wins come from the theme and image pipeline. Use a lean OS 2.0 theme, keep homepage sliders to a single image (or replace them with a static hero), and let Shopify's image CDN serve responsive WebP. Full playbook in how to speed up your Shopify store.
How to Verify the Fix
One Lighthouse run is not proof. Lighthouse throws a synthetic load at your page from one location, and the number moves with server load, network jitter, and time of day. The number Google actually uses for rankings is the 75th-percentile LCP over the last 28 days of real Chrome visits, aggregated in the Chrome User Experience Report.
Immediate check (lab). Run PageSpeed Insights on the fixed URL three times in incognito. If the lab LCP has dropped by more than the noise floor (roughly 200 ms), the fix is working.
Field verification (28-day window). CrUX only updates its rolling 28-day dataset once per day, and a single fixed page needs enough real visits over that window to move the average. On low-traffic pages, expect two to four weeks before CrUX field data reflects the fix. On high-traffic pages, expect one to two weeks for the trend line to move.
Track the trend, not the number. Use Search Console's Core Web Vitals report or a RUM tool (SpeedCurve, Vercel Analytics, Chrome UX Dashboard) to watch the 75th-percentile LCP week over week. A single day's spike is noise; three straight weeks of downward movement is the fix taking effect.
Frequently Asked Questions
What is a good LCP score?
Google's thresholds are 2.5 seconds or less for good, 2.5 to 4.0 seconds for needs improvement, and above 4.0 seconds for poor, measured on mobile at the 75th percentile of real visits. To pass Core Web Vitals, 75% of your visits over the last 28 days must have an LCP under 2.5 seconds.
Why is my LCP high even though my page feels fast?
The page probably feels fast to you because you have a fast device and a warm connection to the site. LCP is measured on mobile at the 75th percentile of your real visitors, many of whom are on slower phones and weaker networks. It only takes one fifth of your traffic on a slow 4G connection or an older Android device to push the 75th-percentile number above 2.5 seconds, even when your own experience is under 1 second.
Does TTFB affect LCP?
Yes, directly. TTFB is the first of the four sub-parts of LCP time. Every millisecond of TTFB is a millisecond of LCP. A TTFB of 800 ms leaves only 1.7 seconds for everything else to reach the 2.5 s good threshold, which is very hard. A TTFB of 150 ms leaves 2.35 seconds, which is comfortable.
Can I preload multiple images?
Technically yes, but it is almost always a mistake. Preloading tells the browser this resource is critical; preloading three hero candidates means the browser aggressively downloads three when it only needs one, and every non-LCP page pays for it. Preload exactly the LCP image with fetchpriority=high, nothing more.
How long until CrUX reflects my LCP fix?
CrUX uses a rolling 28-day window, so a single fixed page needs enough traffic over that window for the new numbers to outweigh the old ones. On high-traffic pages, expect the 75th-percentile LCP to trend down within one to two weeks. On low-traffic pages, expect two to four weeks. Do not judge the fix on the first day - watch the trend line, not a single data point.