seo & ai search · playbook
Core Web Vitals and page speed for marketers
What Core Web Vitals measure, how much they matter for SEO, and how to find and fix the slow pages and tags marketing owns.
Key takeaways
- Core Web Vitals are a real ranking input but a small one: relevance comes first, and good scores never guarantee top positions.
- A page passes only if LCP ≤ 2.5s, INP ≤ 200ms and CLS ≤ 0.1, all measured at the 75th percentile of real-user field data, with mobile and desktop judged separately.
- Field data (CrUX, Search Console, the top of PageSpeed Insights) is the score that counts; Lighthouse lab scores are for debugging.
- Triage by page template and traffic, not URL by URL. Fixing one template fixes every page built on it.
- Marketing owns many of the usual culprits: tag manager bloat, chat widgets, video embeds, consent banners, A/B testing scripts and late-loading popups.
Core Web Vitals (CWV) are three numbers Google uses to describe what a page feels like to real visitors: how fast the main content shows up, how quickly the page responds when someone clicks or taps, and how much the layout jumps around while it loads. Developers usually own the code, but marketing owns a surprising share of what makes pages slow, including tags, pixels, chat widgets, embeds, popups and testing tools. This guide covers what the metrics mean, how much they matter for search, how to read the reports without getting lost, and how to turn a red score into a ticket a developer will actually pick up.
How much Core Web Vitals matter for SEO
Here’s the honest version. Google says “Core Web Vitals are used by our ranking systems,” and that its core ranking systems “look at a variety of signals that align with overall page experience.” It’s equally clear about the limits:
- Relevance wins. “Google Search always seeks to show the most relevant content, even if the page experience is sub-par.” A slow page that answers the query better can outrank a fast page that doesn’t.
- There is no single page experience score. Google says “there is no single signal.” Page experience is a set of things (Core Web Vitals, HTTPS, displaying well on mobile, avoiding intrusive interstitials, not burying the main content under ads), and only Core Web Vitals is named as something the ranking systems use directly. Google says the other page experience aspects “don’t directly help your website rank higher.”
- Passing is not a ranking guarantee. Good scores “doesn’t guarantee that your pages will rank at the top of Google Search results.”
Google doesn’t use the word “tiebreaker,” but that’s the most useful way to think about it. When many pages are similarly relevant, Google says “having a great page experience can contribute to success in Search.” Treat CWV as hygiene: fix pages that are clearly failing, especially high-value ones, but don’t expect a jump in rankings from going from “good” to “slightly better.” The stronger reason to care is that the same problems that fail CWV (a hero that takes five seconds to appear, a form that lags when tapped, a button that moves just as someone reaches for it) also hurt conversion on your landing pages.
The three metrics and their thresholds
| Metric | What it measures | Good | Needs improvement | Poor |
|---|---|---|---|---|
| LCP (Largest Contentful Paint) | Loading: when the largest image or text block in the viewport finishes rendering | ≤ 2.5 s | 2.5–4.0 s | > 4.0 s |
| INP (Interaction to Next Paint) | Responsiveness: delay between a click, tap or key press and the next visual update | ≤ 200 ms | 200–500 ms | > 500 ms |
| CLS (Cumulative Layout Shift) | Visual stability: how much visible content moves unexpectedly | ≤ 0.1 | 0.1–0.25 | > 0.25 |
A few rules decide how these numbers are judged:
- 75th percentile of real visits. Each metric is assessed at the 75th percentile of page loads, so a page is “good” when at least three out of four visits hit the target. Your fast office laptop doesn’t count; the slow phone on a train does.
- Mobile and desktop are separate. Google’s guidance is to segment the 75th percentile across mobile and desktop. A template can pass on desktop and fail on mobile, and mobile is usually where it fails.
- All three must pass. In PageSpeed Insights, a page passes the Core Web Vitals assessment only when LCP, INP and CLS are all “good” at the 75th percentile. If there isn’t enough INP data, LCP and CLS decide it. In Search Console, a group of pages takes the status of its worst metric.
INP replaced FID. Interaction to Next Paint replaced First Input Delay as a Core Web Vital on March 12, 2024. Older guides, audits and agency decks that talk about FID are out of date. INP is stricter: FID only measured the delay before the first interaction started being processed, while INP looks at clicks, taps and key presses across the whole visit and reports roughly the slowest one (ignoring outliers). Hovering and scrolling don’t count as interactions.
What can be the LCP element. Images, <image> elements inside SVGs, video (via the poster image or first frame), elements with a CSS url() background image, and block-level text. On a typical B2B landing page that’s the hero image, a background video or the headline.
Field data vs. lab data
Most of the confusion around page speed comes from mixing up two kinds of data.
Field data comes from real Chrome users. The Chrome UX Report (CrUX) collects it from Chrome users who have opted in (usage statistics reporting on, browser history synced, no sync passphrase) on desktop and Android. Chrome on iOS, Android WebView apps and other Chromium browsers such as Edge are not included. CrUX reports a rolling 28-day window, and pages or origins only appear once they’re “sufficiently popular.” Google doesn’t disclose the exact threshold.
You see field data in three places:
| Tool | What it shows | Best for |
|---|---|---|
| Search Console → Core Web Vitals report | CrUX data grouped into URL groups of “pages that have a similar user experience,” split by mobile and desktop | Seeing which templates fail across the whole site |
| PageSpeed Insights, top section (“Discover what your real users are experiencing”) | CrUX for one URL over the trailing 28 days, falling back to origin-wide data if the URL lacks enough traffic | Checking one important page |
| CrUX API / CrUX dashboards | The same 28-day rolling data as phone, tablet and desktop, updated daily on a best-effort basis | Tracking over time and reporting |
Lab data comes from a single simulated test, usually Lighthouse (the lower section of PageSpeed Insights, or Chrome DevTools). It loads the page once on an emulated device and network and produces the familiar 0–100 performance score.
Why they disagree. web.dev lists the reasons:
- One lab device and network vs. the full spread of real devices and connections.
- Different screen sizes make different elements the LCP element.
- Lab tests usually load with a cold cache, while returning visitors have cached files.
- Real users scroll and interact, which changes LCP and adds layout shifts the lab never sees.
- Personalized content, “including targeted ads and A/B tests,” changes what loads for different users.
- INP depends on when real people interact, which a lab run can’t predict. Lighthouse shows Total Blocking Time (TBT) as a stand-in. web.dev says a low TBT often correlates with a low INP, but TBT isn’t a substitute for INP.
The rule: field data is the score that counts, lab data is for debugging. A Lighthouse score of 95 doesn’t mean you pass, and a score of 40 doesn’t mean you fail. If field CLS is much worse than lab CLS, the shifts are probably happening after load (late popups, injected banners, lazy-loaded content as people scroll), which lab tests mostly miss.
Triage by template and traffic, not URL by URL
A marketing site might have thousands of URLs but only a dozen templates: home, product pages, solution pages, blog posts, landing pages, resource/gated-content pages, docs, pricing, comparisons. Performance problems almost always come from the template and the scripts it loads, not from individual pages.
- Start in Search Console’s Core Web Vitals report, mobile first. Open the Poor and Needs improvement rows. Search Console already groups similar URLs. Click into a group, note the example URLs, and map each group to a template.
- Weight by business value. For each failing template, pull the sessions and conversions it drives from analytics. A failing blog template with 60% of organic traffic matters more than a failing legal page. A failing paid landing page template matters even if it gets little organic traffic, because you’re paying for every slow visit.
- Find the failing metric for each template. LCP, INP and CLS have different causes and different owners, so note which one fails.
- Pick one representative URL per template with enough traffic to have its own field data in PageSpeed Insights, and use it for lab debugging.
- Check for site-wide causes first. If every template fails the same metric, look at what every page shares: the tag manager container, the consent banner, the chat widget, fonts, the header. One fix there moves everything.
LCP: common causes and fixes
web.dev breaks LCP into four parts: time to first byte (server response), resource load delay (how long before the browser starts fetching the LCP image), resource load duration (downloading it) and element render delay (time between download and display). On a well-optimized page, the two “delay” parts should be close to zero.
| Cause | What it looks like | Fix |
|---|---|---|
| Hero image discovered late | Hero set as a CSS background, injected by JavaScript, or behind a lazy-loading library that hides the src | Put the hero in the HTML as an <img> with src/srcset, or preload it if it must stay in CSS |
| Hero image lazy-loaded | loading="lazy" on the first image on the page | Never lazy-load the LCP image. Lazy-load images below the fold only |
| Hero not prioritized | The browser fetches scripts and other images first | Add fetchpriority="high" to the hero image. Use it on one or two images at most, since prioritizing everything prioritizes nothing |
| Oversized images | Full-resolution photos and uncompressed hero files | Serve right-sized, compressed images and a CDN with efficient caching |
| Render-blocking scripts and CSS | Synchronous scripts in <head>, large stylesheets | Defer non-critical scripts, reduce or inline small critical CSS |
| A/B testing hiding the page | Content hidden until the testing script decides which variant to show | Limit tests on high-traffic templates, run tests server-side where possible, and keep the hide period short |
| Client-side rendering | The page is an empty shell until JavaScript builds it | Server-side render or prerender marketing pages |
| Slow server / redirects | High time to first byte, redirect chains from campaign links | Cache pages at a CDN, link to the final URL, avoid cache-busting query parameters that bypass the CDN |
| Web fonts blocking text | Headline invisible until the font downloads | Use a font-display value other than auto or block, and preload the one or two critical fonts |
One marketing-specific trap is redirects in campaign URLs. A link shortener, then a tracking redirect, then an HTTP-to-HTTPS hop each delay the moment the page’s HTML even starts arriving, and web.dev’s LCP guidance is to minimize redirects. Point ads and emails at the final URL.
INP: common causes and fixes
INP is the sum of three phases: input delay (the main thread is busy, so the click waits), processing (the code that runs in response) and presentation delay (painting the result). On script-heavy marketing pages, input delay is a common problem: the main thread is busy running scripts, and the visitor’s tap waits in line.
A long task is any piece of work that blocks the main thread for more than 50 milliseconds. Every script a page loads takes time to evaluate, and web.dev notes that script evaluation “can cause long tasks” that “interfere with user interactions and affect a page’s INP during load.” That’s why a page with a dozen marketing tags often feels sluggish for the first few seconds, even when it looks fully loaded.
| Cause | Fix |
|---|---|
| Large tag manager container | Audit and remove dead tags. web.dev reports “a correlation between the size of tag managers and poorer INP scores” |
| Non-essential tags firing early | Fire analytics, retargeting and heatmap tags after the window has loaded rather than on page start |
| Chat widgets loading their full app on page load | Load a lightweight lookalike button and load the real widget on hover, click or idle |
| Heavy JavaScript in form or menu click handlers | Developers break up the work and yield to the main thread so the visual update happens first |
| Multiple tag manager containers on one page | Consolidate to one container per page |
For marketers, the biggest INP lever is loading less third-party JavaScript, and loading it later.
CLS: common causes and fixes
Layout shifts are the easiest problem to see and often the easiest to fix, because they usually come from content arriving without a reserved space.
| Cause | Fix |
|---|---|
| Images and videos without dimensions | Always set width and height attributes (or CSS aspect-ratio) so the browser reserves space |
| Ads, embeds and iframes (video, social posts, forms, calendars) | Reserve space with a fixed-size container or min-height. Don’t collapse the space if nothing loads, since removing reserved space shifts the layout too |
| Banners and announcements injected at the top | Reserve the space in the initial layout, or overlay the banner instead of pushing content down. Content injected higher in the viewport causes bigger shifts |
| Consent banners that push content | Use a bottom overlay or modal rather than inserting the banner into the page flow |
| Web fonts swapping | Use metric-matched fallback fonts (e.g. size-adjust), font-display: optional and preloading for critical fonts |
| Embedded forms that resize on load | Give the form container a fixed or minimum height that matches the rendered form |
One useful detail: layout shifts within 500 ms of a user’s click or tap don’t count, so an accordion that opens when clicked is fine. Content that appears on its own while someone is reading or scrolling still counts.
The marketing-owned culprits
Most of these never go through a code review because they’re added through a tag manager or a vendor’s snippet. web.dev’s tag best practices article puts it plainly: “tags get added but are rarely removed.”
Tag managers and pixels.
- Audit the container regularly (how often depends on how fast your tags change; quarterly is a sensible default). Give each tag an owner and a purpose, and pause or remove anything without one.
- Remove unused tags. Don’t just block them with a trigger exception, which leaves the code in the container. Clean up their triggers and variables too.
- Fire non-essential tags after
Window Loaded. web.dev: “the earlier that a tag fires, the greater it impacts performance.” - Don’t load anything visible through the tag manager: no cookie notices, hero images or site features.
- Load big vendor libraries via an external script URL, not pasted into a custom HTML tag.
- On larger sites, consider server-side tagging, which “removes vendor code from the client.”
- Test container changes for layout shifts and long-running scripts before publishing, and require approval, as you would for code.
Chat widgets. Chat tools often load a full application on every page. A facade, a static button that looks like the widget and loads the real one on interaction, is the pattern Chrome’s Lighthouse documentation describes for chat, video and social widgets (the facades audit itself was removed in Lighthouse 13, but the advice stands). The trade-off: the facade won’t show live features like unread badges until the real widget loads. Also ask whether chat needs to be on every page, or only on pricing, demo and product pages.
Video embeds. Many popular embeds include over 100 KB of JavaScript. Use loading="lazy" on below-the-fold iframes (web.dev estimates this saves about 500 KB on initial load for a YouTube embed), or a facade that shows a thumbnail and loads the player on click. Always set the embed’s dimensions so it doesn’t shift the layout. Autoplaying background videos in the hero can also become the LCP element, so test them.
Consent banners. Load the consent script asynchronously and directly in the page HTML, not through the tag manager, which delays it. A bottom-of-screen overlay “typically does not cause CLS.” On mobile, a text-heavy banner can itself become the LCP element, so keep it short.
A/B testing and personalization. Client-side testing tools often hide the page until the variant is chosen, which delays LCP, and swapping content after render causes CLS. Run tests only where you have a live hypothesis, remove the snippet from templates with no running tests, and prefer server-side or edge-based testing for high-traffic templates.
Popups, exit-intent and slide-ins. Overlay them rather than inserting them into the flow, delay them until after the page has loaded, and remember that intrusive interstitials are themselves a page experience concern.
How to write a fix ticket a developer will act on
“The site is slow, PageSpeed says 42” will sit in the backlog forever. A good ticket is specific, scoped to a template, and states the evidence and the expected outcome.
Template:
Title: [Template] fails [metric] on mobile: [suspected cause]
Scope: Blog post template (about 1,400 URLs, ~55% of organic sessions). Search Console mobile URL group: Poor, LCP issue.
Evidence: Field LCP p75 (PageSpeed Insights, example URL /blog/example-post): 3.9 s. Lighthouse shows the LCP element is the hero image, which is lazy-loaded and has no
fetchpriority. Screenshot or trace attached.Proposed change: Remove
loading="lazy"from the first image on the template, addfetchpriority="high", serve a resized version for mobile widths.Acceptance criteria: Lighthouse mobile run on 3 example URLs shows the hero is not lazy-loaded and is requested early. Field LCP p75 for the URL group is ≤ 2.5 s within 28 days of release.
Out of scope: Don’t change the hero’s visual design.
Keep it to one template and one metric per ticket, pair field evidence (real users are affected) with a lab finding (here’s the cause), and include traffic or conversion value so it can be prioritized. If the cause is a tag, skip the ticket: remove or delay it yourself in the tag manager.
How to verify a fix
Field data lags. Because CrUX is a rolling 28-day window, a fix shipped today is blended with 27 days of pre-fix visits tomorrow. The field number moves gradually and only fully reflects the change about four weeks after release.
- Verify in the lab immediately. Run Lighthouse on the representative URLs before and after. Confirm the specific cause is gone (hero not lazy-loaded, tag no longer firing early, banner no longer shifting content).
- Annotate the release date in your reporting so the eventual field change can be attributed.
- Start validation in Search Console. After fixing a URL group, use “Start tracking” in the Core Web Vitals report to begin a 28-day monitoring session. This doesn’t trigger recrawling or reindexing. It only tracks whether the issue persists.
- Watch the CrUX trend weekly, not daily. Expect a gradual slope over four weeks, not a step.
- Consider real-user monitoring if you need faster feedback or data for pages below CrUX’s traffic threshold. The open-source
web-vitalsJavaScript library can send the same metrics from your own visitors to your analytics.
Checklist
Measure
- Check Search Console’s Core Web Vitals report for mobile and desktop separately
- Map each failing URL group to a template
- Note which metric fails per template (LCP, INP or CLS)
- Weight templates by sessions and conversions, including paid landing pages
- Pick one high-traffic representative URL per template
LCP
- Hero image is in the HTML, not a CSS background or JS-injected
- Hero image is not lazy-loaded and has
fetchpriority="high" - Images are right-sized and compressed
- Campaign links point at final URLs with no redirect chains
- No A/B test is hiding the page on high-traffic templates without a running experiment
INP
- Tag manager container audited; unused tags removed, not just blocked
- Non-essential tags fire after
Window Loaded - One tag manager container per page
- Chat widget uses a facade or is limited to the pages that need it
CLS
- All images, videos and iframes have dimensions or reserved space
- Consent banner, popups and announcement bars overlay the page instead of pushing content
- Embedded forms and ad slots have a minimum height
- Web fonts use fallbacks that minimize the swap shift
Sources
- Google Search Central, Understanding page experience in Google Search results: https://developers.google.com/search/docs/appearance/page-experience
- Google Search Central, Understanding Core Web Vitals and Google search results: https://developers.google.com/search/docs/appearance/core-web-vitals
- web.dev, Web Vitals: https://web.dev/articles/vitals
- web.dev, How the Core Web Vitals metrics thresholds were defined: https://web.dev/articles/defining-core-web-vitals-thresholds
- web.dev, Interaction to Next Paint becomes a Core Web Vital on March 12: https://web.dev/blog/inp-cwv-march-12
- web.dev, Largest Contentful Paint (LCP): https://web.dev/articles/lcp
- web.dev, Interaction to Next Paint (INP): https://web.dev/articles/inp
- web.dev, First Input Delay (FID): https://web.dev/articles/fid
- web.dev, Why lab and field data can be different: https://web.dev/articles/lab-and-field-data-differences
- web.dev, Optimize LCP: https://web.dev/articles/optimize-lcp
- web.dev, Optimize INP: https://web.dev/articles/optimize-inp
- web.dev, Optimize CLS: https://web.dev/articles/optimize-cls
- web.dev, Optimize long tasks: https://web.dev/articles/optimize-long-tasks
- web.dev, Script evaluation and long tasks: https://web.dev/articles/script-evaluation-and-long-tasks
- web.dev, Best practices for tags and tag managers: https://web.dev/articles/tag-best-practices
- web.dev, Best practices for cookie notices: https://web.dev/articles/cookie-notice-best-practices
- web.dev, Best practices for using third-party embeds: https://web.dev/articles/embed-best-practices
- Chrome for Developers, Lazy load third-party resources with facades: https://developer.chrome.com/docs/lighthouse/performance/third-party-facades
- Chrome for Developers, CrUX methodology: https://developer.chrome.com/docs/crux/methodology
- Chrome for Developers, CrUX API: https://developer.chrome.com/docs/crux/api
- PageSpeed Insights, About PageSpeed Insights: https://developers.google.com/speed/docs/insights/v5/about
- Search Console Help, Core Web Vitals report: https://support.google.com/webmasters/answer/9205520
- GoogleChrome web-vitals library: https://github.com/GoogleChrome/web-vitals