This guide assumes Search Console or PageSpeed Insights has already told you something is failing. The targets are an LCP of 2.5 s or less, an INP of 200 ms or less and a CLS of 0.1 or less, measured at the 75th percentile of real visits. If those terms or the 75th percentile rule are new, read what Core Web Vitals measure and how Google scores them first.
Step 1: Find the failing metric and template in field data
Start with field data, because that is what Google scores (web.dev):
- Open the Core Web Vitals report in Search Console. URLs are grouped by similar experience, and each group takes the status of its worst metric (Search Console Help). Note which metric fails, on mobile or desktop, and an example URL.
- Run that example URL through PageSpeed Insights. The top section is CrUX field data; the Lighthouse run below it is a lab test you can use to diagnose.
- Fix the group with the most Poor URLs first. The status covers the whole group, so one fix to a shared template can clear many URLs.
Lighthouse cannot measure INP, because nobody interacts with the page in a lab run. It shows Total Blocking Time (TBT) instead, and a page can pass TBT and still fail INP (web.dev). For INP, record real interactions in the Chrome DevTools Performance panel, which shows your local LCP, CLS and INP live and can compare them with CrUX field data (Chrome for Developers).
Step 2: Fix LCP
LCP time splits into four parts (web.dev): time to first byte (TTFB), the delay before the LCP resource starts downloading, its download time, and the delay before it renders. Find which part is large before changing anything; shrinking one part can just move the time into another.
Let the browser find the LCP image early
Put it in the HTML as a normal <img>, give it fetchpriority="high", and never lazy-load it:
<img
src="/images/hero.webp"
width="1200"
height="600"
alt="Describe the image"
fetchpriority="high"
/>
If the image is only referenced from CSS or JavaScript (a CSS background, for example), preload it so the browser does not wait for those files:
<link rel="preload" as="image" href="/images/hero.webp" type="image/webp" fetchpriority="high" />
Set fetchpriority="high" on one or two images at most; on more, it stops helping (web.dev).
Cut server response time
TTFB is not a Core Web Vital, but it is the first part of LCP, and web.dev treats 0.8 s or less as good (web.dev). The fixes in order (web.dev): hosting that can handle your traffic, a CDN, edge caching with the right Cache-Control headers, removing same-origin redirects, and 103 Early Hints if your backend needs a long time to build the page.
Remove render-blocking work
Keep CSS small and avoid synchronous <script> tags in the <head>; web.dev says they are almost never necessary.
Serve smaller images
Use AVIF or WebP with responsive srcset sizes. Google reports lossy WebP files are 25-34% smaller than comparable JPEGs at equivalent quality, and lossless WebP is 26% smaller than PNG (Google for Developers). Download time is rarely the biggest part of LCP, though, so check the breakdown first.
Step 3: Fix INP
An interaction has three parts: input delay (the main thread is busy when the user clicks), processing (your event handlers run) and presentation delay (the browser renders the next frame) (web.dev). Long tasks, meaning any task over 50 ms on the main thread, hold up all three: while one runs, the browser cannot respond to input or paint (web.dev).
Do the visible update first, then yield
Update the UI, hand control back to the browser so it can paint, and run the rest in a new task. scheduler.yield() does this with a prioritised continuation; browsers without it can fall back to setTimeout:
function yieldToMain() {
if (globalThis.scheduler?.yield) {
return scheduler.yield();
}
return new Promise((resolve) => setTimeout(resolve, 0));
}
button.addEventListener('click', async () => {
updateUI(); // visible change first
await yieldToMain(); // let the browser paint
saveToServer(); // non-visual work in a separate task
sendAnalytics();
});
This pattern is adapted from web.dev's guide to long tasks, which lists scheduler.yield() as supported in Chrome and Edge 129 and Firefox 142, but not yet in Safari.
Move heavy computation to a Web Worker
Workers run in parallel with the main thread, but they cannot touch the DOM, so they suit parsing, sorting and number crunching rather than UI work (web.dev).
Keep the DOM small
Rendering work grows with DOM size. content-visibility: auto lets the browser skip rendering off-screen sections until they approach the viewport (web.dev).
Step 4: Fix CLS
These fixes come from web.dev's CLS guide:
- Give every image and video
widthandheightattributes, or reserve the space with CSSaspect-ratio. - Reserve space for ads and embeds with
min-height, and keep that space even when no ad fills it. Removing the slot causes a shift too. - Put content that loads late (banners, recommendations) further down the page, or load it when the user asks for it. Shifts within 500 ms of a tap, click or key press are not counted.
- For web fonts, both swap and invisible-text loading can shift the layout. Use
font-display: optional, or match the fallback font's size to the web font withsize-adjustand the related overrides, and preload the main font file. - Animate with
transforminstead oftoporleft; transform animations don't count toward CLS. - Keep pages eligible for the back/forward cache, so a return visit shows the finished page with no shifts.
<!-- Space is reserved before the ad script runs -->
<div class="ad-slot" style="min-height: 250px;"></div>
Step 5: Reduce third-party script cost
Analytics, chat widgets, tag managers and ad scripts run on the same main thread as your own code. Following web.dev's guidance on third-party JavaScript:
- Remove any script that doesn't add clear value.
- Load the rest with
async, ordeferfor less critical ones, unless a script has to run before the page renders. - Use
<link rel="preconnect">only for the most critical third-party origins anddns-prefetchfor the others. - Lazy-load below-the-fold embeds with the native
loading="lazy"attribute.
Step 6: Cache and compress static files
Give fingerprinted files (for example app.3f9a2c.js) a one-year cache and make the browser revalidate anything unversioned, such as HTML (web.dev):
# Versioned assets
Cache-Control: max-age=31536000, immutable
# HTML and other unversioned URLs (pair with an ETag)
Cache-Control: no-cache
Some browsers ignore immutable; it is an optional extra. Serve text files (HTML, CSS, JS, JSON, SVG) compressed with Brotli where the browser supports it, with gzip as the fallback. Check the content-encoding response header in the DevTools Network tab to confirm (Chrome for Developers).
Step 7: Monitor the result with field data
CrUX uses a rolling 28-day window, so a fix takes up to four weeks to show fully in Search Console. To see the effect sooner, collect your own field data with Google's web-vitals library:
import { onCLS, onINP, onLCP } from 'web-vitals';
function send(metric) {
navigator.sendBeacon('/analytics', JSON.stringify(metric));
}
onCLS(send);
onINP(send);
onLCP(send);
Then:
- Add Lighthouse CI to your build to catch lab regressions on every commit.
- In Search Console, click Start tracking on the fixed issue. It starts a 28-day monitoring period; it does not trigger re-indexing (Search Console Help).
| Tool | Data | Measures INP? | Cost |
|---|---|---|---|
| PageSpeed Insights | CrUX field data plus a Lighthouse lab run | In the field section only | Free |
| Search Console Core Web Vitals report | CrUX field data, grouped by URL | Yes | Free |
| Chrome DevTools Performance panel | Your live session, optional CrUX comparison | Yes, when you interact | Free |
| Lighthouse CI | Lab runs in CI | No (uses TBT) | Free, open source |
| web-vitals library | Field data from your own visitors | Yes | Free, open source |
Will better scores lift rankings?
Core Web Vitals are used by Google's ranking systems, but relevance comes first, and Google says good scores don't guarantee top positions (Google Search Central). Fix failing metrics because visitors feel them, and judge the work by your field data and conversions rather than by a ranking jump. The Core Web Vitals guide covers what Google says in more detail.
Get help with the fixes
If the failing template sits in a theme, framework or hosting setup you'd rather not touch yourself, our speed optimization service starts with a free review. On WordPress, our WordPress speed and caching work covers the theme, plugins and hosting side. If the trouble goes beyond speed, for example redirects, canonicals or pages Google has not indexed, our technical SEO audits for UK businesses look at those signals together.
