Phase 2: Performance Engineering (Core Web Vitals)
One of the most common points of confusion for technical SEOs working on Core Web Vitals is this: PageSpeed Insights says my LCP is 4.2 seconds, but Lighthouse shows 1.8 seconds on the same page. Which one is right? Both are right — they are measuring fundamentally different things. Understanding the distinction between field data and lab data is not a minor technicality. It determines which numbers you report, which numbers you optimise for, and which numbers actually affect your Google rankings.
Every performance measurement tool is either collecting data from real users in real conditions (field data), or it is simulating a page load in a controlled environment (lab data). These are not two ways of measuring the same thing — they are measuring different phenomena.
Field data is collected from actual users as they browse your site in the wild. Each time a real person loads a page in Chrome, the browser can (with user consent, via the Chrome User Experience Report opt-in) record performance metrics like LCP, INP, and CLS and submit them anonymously to Google.
Google aggregates this data into the Chrome User Experience Report (CrUX). CrUX is the canonical source for field data and is the dataset that Google uses to assess your Core Web Vitals for the Page Experience signals.
web-vitals library — your own
first-party field data, collected from your users and sent to your analytics platform. This gives you
segmentation by page template, device type, geography, and more that CrUX does not expose.
Lab data is produced by running a synthetic page load in a controlled, reproducible environment. A tool like Lighthouse launches a headless Chromium browser, loads the page with a specified network throttle and CPU slowdown profile, and measures performance metrics under those fixed conditions.
Because the conditions are fixed and repeatable, lab data is excellent for debugging and for measuring the effect of a specific code change. But because it uses synthetic conditions, it will rarely match the experience of any individual real user.
Field and lab data almost always show different numbers. This is not a bug. Here are the most common reasons for divergence:
Lighthouse's default mobile test simulates a "mid-tier mobile device" with a 4x CPU slowdown applied to your development machine's CPU. Your actual user base might be 60% desktop, skewing field data faster — or 40% low-end Android phones in markets with poor connectivity, skewing it slower. CrUX reflects your real device mix. Lighthouse reflects one synthetic device profile.
Lighthouse runs with an empty cache by default (a cold load). Many of your real users are returning visitors who have assets cached. Cache hits dramatically reduce load times, bringing field data LCP well below lab data. Conversely, if your caching strategy is broken, real users may never benefit from caching even though Lighthouse doesn't notice.
An advertising network, live chat widget, or A/B testing platform may respond slowly or block the main thread for real users in specific geographies or on specific devices. In a lab test run from a data centre, the same script may respond instantly or behave differently. Third-party variability is one of the biggest sources of field/lab divergence on publisher and e-commerce sites.
Lighthouse tests the public, logged-out version of a page. If a significant share of your users are logged in and receive personalised content (heavier server-side queries, more DOM nodes, different LCP elements), their experience — captured in field data — may be substantially different.
Real networks are noisy. A user on a train whose connection momentarily drops, a user on congested Wi-Fi in a coffee shop, a user in a country where your origin server is geographically far — all of these experiences end up in your CrUX distribution. The Lighthouse "Fast 3G" or "Slow 4G" throttle is a single approximation. WebPageTest lets you run from multiple locations, which is closer to reality, but still a sample.
Interaction to Next Paint requires a real user interaction. Lab tools cannot trigger it. Lighthouse reports Total Blocking Time (TBT) as a correlated diagnostic — a high TBT usually indicates a poor INP — but the relationship is not one-to-one. You can have a reasonable TBT and still fail INP if a specific interaction handler is particularly slow.
The answer depends on what you are trying to do. Both types of data are necessary. They are not alternatives — they are complements.
| Decision | Use | Why |
|---|---|---|
| Is this page passing CWV for Google ranking purposes? | Field data (CrUX / GSC) | Google uses CrUX, not Lighthouse scores, for ranking signals. |
| Diagnosing which resource is causing slow LCP | Lab data (Lighthouse / WebPageTest / DevTools) | Lab tools give you waterfalls, filmstrips, and element-level attribution. Field data only gives you a metric value. |
| Did my code change improve performance? | Lab data first, then confirm with field data after 28+ days | Lab data gives you an immediate signal; field data confirms the real-user impact once the 28-day window matures. |
| Prioritising which page templates to fix first | Field data (GSC CWV report / CrUX by URL) | Tells you which templates are actually failing for real users, not just in synthetic tests. |
| Catching a performance regression before it ships | Lab data (Lighthouse CI in CI/CD) | Field data is not available for unreleased code. Lab data in CI catches regressions at the source. |
| Investigating INP problems | Field data (RUM / web-vitals library) | INP requires real interactions; only field data can measure it directly. |
| Reporting to stakeholders on business impact | Field data | Stakeholders care about what real users experience. Lighthouse scores are not user experience. |
CrUX data is a 28-day rolling average. This means:
The professional workflow for this is:
web-vitals library so you can see field improvement
in your own analytics within days of the fix, without waiting for CrUX.Without your own RUM, you are flying blind between shipping a fix and waiting for CrUX to reflect it.
It is worth stating this explicitly, because the Lighthouse score (out of 100) is seductive — it is a single, simple number that is easy to report and easy to game.
Google does not use the Lighthouse performance score as a ranking signal. It uses CrUX field data for the three Core Web Vitals (LCP, INP, CLS) at the p75 threshold. A page with a Lighthouse score of 95 can still fail CWV in field data if real users on low-end devices are having a poor experience. A page with a Lighthouse score of 62 can pass CWV in field data if the majority of its users are on fast desktop connections and cached loads.
Optimise for real users. Use field data as your success criterion. Use lab data as your diagnostic tool.
The web-vitals JavaScript library (maintained by Google) is the standard way to collect
your own first-party RUM. It reports LCP, INP, CLS, FCP, and TTFB from real user sessions.
A minimal implementation looks like this:
<!-- Install via npm or include via CDN -->
<!-- npm install web-vitals -->
<script type="module">
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'https://unpkg.com/web-vitals/dist/web-vitals.attribution.js';
function sendToAnalytics({ name, value, rating, id, navigationType }) {
// Send to your analytics endpoint, e.g. Google Analytics 4, Datadog, a custom endpoint
console.log({ metric: name, value, rating, id, navigationType });
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);
</script>
Key decisions when setting up RUM:
web-vitals.attribution.js) to capture
which element caused LCP, which interaction triggered INP, and which layout shift source contributed
most to CLS. Without attribution data, RUM tells you that there is a problem but not
where it is.PageSpeed Insights is often misread because it contains both field and lab data on the same page. Here is how to distinguish them:
Imagine you run PSI on a product page and see:
How do you interpret this? The page is actually failing for real users despite looking fine in Lighthouse. Likely causes to investigate:
The diagnostic path: use WebPageTest to run from multiple locations and with a real mobile device profile. Check your RUM data filtered to mobile users. Look at the CrUX breakdown by connection type. Identify the specific condition that is causing the divergence, then fix the root cause — not the Lighthouse score.
| Field Data (CrUX / RUM) | Lab Data (Lighthouse / WebPageTest) | |
|---|---|---|
| Source | Real users in real conditions | Synthetic page load in controlled conditions |
| Used by Google for ranking? | Yes | No |
| Requires live traffic? | Yes (CrUX needs volume; RUM needs deployment) | No |
| Reflects third-party variability? | Yes | Partially / inconsistently |
| Can measure INP? | Yes | No (TBT is a proxy) |
| Update frequency | 28-day rolling window (CrUX); near-real-time (RUM) | On-demand, instant |
| Best for | Confirming pass/fail, reporting, monitoring trends | Diagnosing root causes, testing fixes, CI regression detection |
| Primary tools | GSC CWV report, PageSpeed Insights (top section), CrUX API, web-vitals library |
Lighthouse, PageSpeed Insights (bottom section), WebPageTest, DevTools Performance panel |
Take a live page with enough traffic to have CrUX data.
web-vitals library on a test or staging page and log the metrics to the
browser console. Trigger an interaction (click a button, open a modal) and observe the INP value
— something no lab tool can capture.
You have completed this module when you can look at any field/lab divergence and articulate the likely real-world causes — and when you can explain to a stakeholder why the Lighthouse score going up does not necessarily mean CWV in Google Search Console will improve.