Core Web Vitals score real visitors, not the clean lab run you keep screenshotting
By The bee2.io Engineering Team at bee2.io LLC

A 98 in Lighthouse and a red Core Web Vitals badge in Search Console are not a contradiction. They are two different measurements from two different worlds, and only one of them is describing the people who actually loaded your homepage on a three-year-old Android phone over patchy 4G.
Core Web Vitals is Google's set of field-measured metrics for loading speed, responsiveness and visual stability: Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS). Each has a "good" threshold that Google publishes and that most tooling (PageSpeed Insights, Chrome DevTools, Search Console) reports against: LCP at or under 2.5 seconds, CLS at or under 0.1, INP at or under 200 milliseconds. Anything past roughly double those numbers - LCP over 4s, CLS over 0.25, INP over 500ms - is classed as poor, with a "needs improvement" band in between.
The metrics aren't opinions about your codebase. They're percentile summaries of what actually happened in Chrome on real devices, which is why a site can pass every synthetic audit and still fail its Core Web Vitals assessment in Search Console.
Field data and lab data disagree on purpose
Lighthouse and DevTools give you lab data: one page load, one simulated network profile, one device emulation, run in isolation. It's reproducible, which makes it useful for debugging, but it's also a controlled experiment, not a census.
Field data comes from the Chrome User Experience Report (CrUX), which aggregates anonymized metrics from real Chrome users who opted into usage statistics. Google's public guidance is that Search Console and the CrUX dataset report the 75th percentile of visits over a rolling 28-day window. That percentile choice matters: it means one in four of your real visits is having a worse experience than the number you're staring at.
This is also why a marketing page can look great in a Lighthouse run from your office fiber connection and still get flagged for CLS in the field: your test never triggered the ad slot that inserts nothing until the user reaches for it.
The metric that got swapped out from under everyone
For years the third pillar of responsiveness was First Input Delay (FID), which measured only the delay before the browser could start processing a user's first interaction. Google replaced FID with INP as the official Core Web Vitals responsiveness metric on March 12, 2024. INP is a meaningfully different measurement: instead of grading only the first click or tap, it tracks the interaction with the worst latency (input to next paint) across the entire page visit.
That's not a rounding change. FID could report a fast number while every subsequent interaction on a JavaScript-heavy single-page app quietly hung the main thread. INP can't be fooled the same way, which is also why sites that never looked at anything past first paint tend to see their INP numbers land worse than their old FID numbers ever did. Any guide still optimizing for FID as a Core Web Vital is describing a metric Google no longer scores that way.
Measuring it without guessing
The field measurements themselves come from Google's web-vitals JavaScript library, which is the reference implementation most other tooling (including PageSpeed Insights and third-party RUM products) builds on to compute LCP, INP and CLS in the browser.
For teams already running Google Tag Manager, wiring that library up by hand isn't the only route. Simo Ahava's Core Web Vitals custom tag template for GTM packages the field measurement into a drop-in tag so metrics can be piped straight into an analytics platform without hand-rolling the JavaScript integration. It's Apache-2.0 licensed and available on GitHub.
Search Console's Core Web Vitals report is the other common entry point, but it groups URLs into cohorts by similar performance characteristics rather than reporting every URL individually, so a fix on one page can take time to show up as a fixed cohort rather than an instantly updated single row.
Where this goes sideways
Search Console shows "no data" or origin-level numbers for a page you just fixed. CrUX only reports a URL once it has enough distinct real-user samples in the 28-day window; low-traffic pages fall back to origin-level aggregates instead, which blend in every other page's performance. The fix isn't in your code, it's in traffic volume and patience; check PageSpeed Insights' own field data panel for the same URL as a faster read, and expect the origin-level number to lag your actual change by weeks.
CLS spikes only in the field, never in a local test. The usual culprit is content that isn't present on the first controlled load: a late-loading ad slot, a web font swap that reflows text, or a promotional banner injected by a script after the initial render. Reserve space with explicit width/height (or aspect-ratio) on images and embeds, and use font-display: optional or matched fallback metrics so font swaps don't shove the layout around.
LCP is "good" in the lab but the real bottleneck element is invisible to the tool. CSS background images aren't eligible LCP candidates the way an <img> or video poster is, so if your actual largest visible content is a CSS background, the tool is scoring some other element entirely, usually a block of text, and telling you it's fine while your real hero image loads late. Check which element PageSpeed Insights actually names as the LCP candidate before trusting the number.
INP looks fine on desktop and terrible on mobile for the same page. INP is dominated by main-thread contention, and mobile CPUs are the ones actually starved by long JavaScript tasks, third-party scripts, and synchronous event handlers. A desktop dev machine with a fast CPU can mask input delay that a mid-range Android device will not; test with CPU throttling in DevTools or against real mobile field data, not just a desktop pass.
Optimizing FID and wondering why Core Web Vitals still fails. FID stopped being a Core Web Vital on March 12, 2024; a page can have a fast FID and a poor INP because INP scores every interaction across the visit, not just the first. If a checklist still references FID as the responsiveness target, it predates the swap.
References
- Simo Ahava's Core Web Vitals custom tag template for Google Tag Manager (Apache-2.0), GitHub: https://github.com/gtm-templates-simo-ahava/core-web-vitals
- Google,
web-vitalsJavaScript library (reference implementation for measuring LCP, INP and CLS in the browser), GitHub: https://github.com/GoogleChrome/web-vitals - web.dev, "Web Vitals" (Google's overview of the Core Web Vitals metrics and thresholds): https://web.dev/articles/vitals
- web.dev, "Interaction to Next Paint (INP)" (Google's explainer on the metric that replaced FID): https://web.dev/articles/inp
- Chrome Developers, "Chrome UX Report (CrUX)" documentation (the field-data source behind Search Console and PageSpeed Insights): https://developer.chrome.com/docs/crux
Important notice. Tap any item to read it in full.
Accuracy is not guaranteed
This article was produced with substantial automated assistance and is published without individual expert verification of every statement. It may contain errors, omissions, oversimplifications, or claims that were accurate when written and have since been superseded. Software, protocols, specifications and best practice in this field change quickly.
Verify before you rely on it
Treat this page as a starting point and a pointer to primary sources, never as an authority in itself. Before acting on anything here, check it against the official documentation, the original publication, or the vendor's own materials, which are linked in the references above. Where this page and a primary source disagree, the primary source is correct and this page is wrong.
No warranty
This content is provided "as is", without warranty of any kind, express or implied, including but not limited to warranties of accuracy, completeness, currency, merchantability, or fitness for a particular purpose.
No liability
To the fullest extent permitted by applicable law, scoutb2.io and its authors accept no liability for any loss or damage whatsoever, whether direct, indirect, incidental, consequential or otherwise, arising from use of or reliance on this article. This expressly includes lost time, lost data, damaged samples or specimens, wasted reagents or compute, failed experiments, equipment damage, and commercial loss.
Not professional advice
Nothing here constitutes professional, scientific, engineering, regulatory, safety or legal advice. You remain solely responsible for your own experimental design, safety assessment, regulatory compliance and data handling, and for any code you run or procedure you perform.
About the illustration
Any image accompanying this article is editorial and decorative. It was produced with generative AI, is not a technical diagram, is not to scale, and is not an accurate depiction of any structure, process or result. Do not read measurements, structures or relationships from it.
Third-party names and links
Product, project and organisation names are the property of their respective owners and are used for identification only. Their mention is not endorsement, affiliation or sponsorship in either direction. External links are provided for convenience and we neither control nor are responsible for third-party content.
Corrections
If you find an error, tell us and we will correct or withdraw the page.
Stop finding issues manually
SCOUTb2 scans your entire site for accessibility, performance, and SEO problems automatically.