Skip to main content

Core Web Vitals: Lab Data vs CrUX Field Data

I ran Lighthouse 28 times on four unchanged pages: scores 63–77, PSI up to 18 points higher, CrUX empty. What the lab and the field each tell you, and how to measure past the noise.

Maciej Zmitrukiewicz Founder, CometWeb

I build CometWeb Insight, a website audit app that ends with a ranked to-do list. I write about what I measure on real sites: performance, SEO, accessibility and CO₂e.

Published: Updated:
18 min read

Recently I ran Lighthouse 28 times on four cometweb.io pages without changing a single line of code. Scores bounced between 63 and 77, and the homepage diagnostics said its LCP element, the largest thing on the first screen, was the word "CometWeb" in the logo.

PageSpeed Insights gave the same pages up to 18 points more, and CrUX, the data from real Chrome users, was empty.

The lab shows how one controlled test behaved, so it's where you hunt for causes. CrUX field data shows what Chrome users experienced over the last 28 days, so it tells you if there's a problem at all.

Key findings - On /pricing, lab LCP moved by 2.1 seconds across seven identical runs. - PageSpeed Insights scored the same URLs 4 to 18 points higher than my local Lighthouse 13.5.0. - On five public sites with CrUX data, lab LCP was 1.06 to 2.18 times the field p75. - developer.chrome.com scored 77 in the lab, while its field INP was 294 ms against a 200 ms threshold.

Lab data, CrUX and RUM answer three different questions

Lab data is a test run under set device and network conditions, while CrUX field data is the aggregated experience of real Chrome users. The third source, your own RUM, you collect yourself.

Table of three sources of performance data. The lab measures one simulated visit and finds the cause, CrUX shows the 75th percentile of Chrome users over 28 days and tells you whether a problem exists, and your own RUM measures your visitors.
The field gives the verdict, the lab finds the cause, and RUM fills the gap when CrUX is empty.web.dev, PSI and CrUX documentation, checked 24 Sep 2026

According to web.dev's Web Vitals overview, Core Web Vitals are field metrics first, though most of them can also be measured in the lab.

Three thresholds apply: LCP up to 2.5 seconds, INP (response to a click or key press) up to 200 ms and CLS (layout jumps) up to 0.1, all at the 75th percentile of page loads, separately for mobile and desktop. Your page passes when it meets all three.

So the field gives you the verdict, because a lab LCP of 6 seconds is the time of one simulated test, and your users may wait less. For the same reason, a lab LCP of 1.5 seconds doesn't pass anything either.

How I measured this

I took four production pages: /, /pricing, /tools/auditor and the WOFF2 vs TTF article (/blog/woff2-vs-ttf), logged out, no cookie consent given. Each one went through Lighthouse 13.5.0 seven times with its default mobile configuration, the same version PageSpeed Insights reported that day.

Lighthouse ran in Playwright's Chromium with a fresh profile per run, so every run was a cold load. Runs went round-robin across the pages, plus three PageSpeed Insights API calls per page with the CrUX data PSI attaches.

Total page weight per URL stayed within 0.31 KB between runs. The main limitation is one machine and one day, 24 September 2026. You'll find the scripts, raw runs and the summary behind every number in the research package.

63–77 Lighthouse Performance scores across 28 local runs of four unchanged pages ?
2.1 s spread of lab LCP on one page (/pricing) across seven identical runs ?
+4 to +18 points by which the PSI median score beat our local median for the same URL ?
0 of 4 pages with CrUX field data (no URL-level and no origin-level data) ?

The same page in seven runs: LCP moved by 2.1 seconds

The median of all 28 runs was 69, and each page spread by 4 to 10 points across its own seven runs.

PageScore, median (min–max)Lab LCP, median (min–max)TBT, median (min–max)CLS
/67 (63–68)6.4 s (6.0–6.8)32 ms (17–43)0 in every run
/pricing67 (65–75)6.3 s (4.9–7.0)3 ms (0–17)0 in every run
/tools/auditor69 (68–72)5.9 s (5.3–6.1)1 ms (0–31)0 in every run
/blog/woff2-vs-ttf70 (69–77)5.7 s (4.8–5.9)0 ms (0–20)0 in every run

The widest spread was on /pricing, where lab LCP went from 4.9 to 7.0 seconds. That's 33% of the page's median, with no code change.

The Lighthouse team documents this in its notes on score variability and recommends the median: over five runs, it's twice as stable as a single run.

Where does the noise come from? The default mobile profile uses simulated throttling, described in Lighthouse's throttling documentation. It loads the page on your real connection and models that load at 150 ms of latency, 1.6 Mbps of bandwidth and a 4x CPU slowdown.

The report also records "observed" timings of the real, unthrottled load, so I kept both.

You'll find the answer on the chart, in the hollow circles that mark the real, unthrottled loads. The four fastest real loads are exactly the four best simulated results in the table: 75 on /pricing, 77 on the WOFF2 article and both 72s on /tools/auditor.

28 local Lighthouse runs shown as squares. In 22 runs the real load painted the LCP element after 1.30 to 1.43 seconds, in the 4 highlighted runs after just 0.29 to 0.35 seconds, and those 4 produced the best simulated scores: 75, 77, 72 and 72.
The faster the real load, the higher the simulated score, so machine and network noise goes straight into your number.Local Lighthouse 13.5.0 runs, 24 Sep 2026 (lab-runs.json)

The simulation derives the throttled load from the real one, so any wobble in your real load goes straight into the score. Test once before a change and once after, and a few points or even two seconds of LCP can be noise. That's why I compare medians of several runs.

PageSpeed Insights scored the same pages 4–18 points higher

The median PageSpeed Insights score was higher than my local median on all four URLs, by 4 to 18 points, and the median lab LCP was 1.6 to 2.6 seconds lower.

PagePSI score, median (min–max)Local score, medianPSI lab LCP, medianLocal lab LCP, median
/71 (63–76)674.5 s6.4 s
/pricing85 (79–85)673.8 s6.3 s
/tools/auditor79 (78–82)694.2 s5.9 s
/blog/woff2-vs-ttf78 (77–81)704.1 s5.7 s

The Lighthouse version and profile were the same, and the reports point to two causes.

The first cause is the slower CPU on PSI's machines, which shows in the benchmark index Lighthouse records for each machine. Mine scored 4,191 to 4,430 across 28 runs, PSI's 499 to 1,338.

It showed in Total Blocking Time, the time the main thread is too busy to respond. One PSI run of the homepage recorded 433 ms, while my local maximum was 43 ms.

According to the Lighthouse performance scoring docs, TBT has weighed 30% of the Performance score since Lighthouse 10. The homepage's three PSI scores ran from 63 to 76 in step with TBT (55–433 ms), while LCP stayed within 150 ms.

The second cause is the network path. PSI runs in a Google data centre, per the About PageSpeed Insights page, and my runs crossed a home connection in Poland. This data can't split the LCP gap between the two, so I won't guess.

So compare lab results only from the same tool, configuration and kind of machine. A PSI score from a client's screenshot and a Lighthouse score from your laptop are two separate measurements, so you can't build a before-and-after pair from them.

CrUX: field data for 0 of 4 cometweb.io pages

Every PSI response came back with an empty field-data section, without URL-level data or an origin-level fallback for cometweb.io. An empty section means "too little data", and it's neither an error nor a zero.

Per the PageSpeed Insights documentation, PSI shows field data when your page has enough CrUX samples. When it doesn't, PSI falls back to your origin, and when the origin is short too, it shows nothing.

To be included at all, your page must be publicly discoverable and "sufficiently popular", which means reaching a minimum number of visitors. Chrome won't tell you that threshold.

Users have to qualify too. The CrUX methodology counts only Chrome users with usage statistics reporting and history sync on, without a Sync passphrase. Chrome on iOS, Android WebView and other Chromium browsers such as Edge are out.

Search Console's Core Web Vitals report then shows "No data available" (Search Console Help), which you should expect if your site is small.

What to report when field data is missing:

  • Field data: N/D, no URL-level or origin-level CrUX data in PSI on [date].
  • Lab data: synthetic, median of [n] runs, [tool, version, configuration].
  • Next step: collect first-party RUM, or keep the lab baseline and repeat it after the change.

Leave missing field data as N/D, because a zero, an assumed pass or a "real-user score" estimated from the lab will mislead whoever reads your report.

On 5 of 5 sites with CrUX data, lab LCP was higher than the field

Where field data does exist, the lab told a different story. I made one PSI call each to five public reference homepages very likely to have CrUX data. All five had URL-level field data in the same response as their lab run.

SiteLab scoreLab LCPField LCP p75Field INP p75Field CLS p75Passes CWV in the field?
web.dev446.8 s3.1 s220 ms0No (LCP, INP)
developer.chrome.com773.8 s3.0 s294 ms0.02No (LCP, INP)
developer.mozilla.org952.7 s1.9 s160 ms0.04Yes
www.w3.org1001.7 s1.1 s89 ms0Yes
www.wikipedia.org1001.4 s1.3 s98 ms0Yes
  • Lab LCP was higher on all five, from 1.06 times on Wikipedia to 2.18 times on web.dev. According to web.dev's article on lab and field data differences, the lab loads your page cold, on an emulated mid-range phone over a slow connection. Real visitors get cached resources, back/forward cache restores and faster devices. How big the gap usually is, five sites can't tell you.
  • MDN's lab LCP was over the threshold, yet it passes in the field. The lab said 2.7 seconds, real Chrome users got 1.9 seconds at the 75th percentile, and the page passes all three Core Web Vitals.
  • developer.chrome.com went the other way. It scored 77 in the lab but fails LCP and INP in the field, with an INP of 294 ms at the 75th percentile. No lab run could've shown you that.

You measure INP in the field, and TBT is a hint

INP measures how your page responds to a click, tap or key press, so it needs a real user. Lighthouse loads your page with nobody at the keyboard.

In its Web Vitals overview, web.dev marks INP as not measurable in Lighthouse and suggests Total Blocking Time as the lab proxy, adding that a better TBT may improve INP in the field. So TBT gets its own column in your report, and the INP column is for field data.

Our pages had a median TBT of 0 to 32 ms locally and 19 to 248 ms on PSI. The test had no interaction, so neither range tells you how our pages respond to someone opening a menu or typing into the auditor.

developer.chrome.com paired 208 ms of lab TBT with a field INP of 294 ms, and web.dev 829 ms of TBT with 220 ms of INP, so the direction was similar but the scales were very different.

You get INP from CrUX if your pages qualify, or from your own RUM. To debug a specific interaction, open DevTools > Performance, record a trace and perform it yourself. I go deeper on the metric in the INP glossary entry.

A CLS of 0 across 28 runs only describes the cold load

All 28 local runs of our pages reported a CLS of exactly 0, and the 12 PSI runs at most 0.00003. But lab CLS only covers layout shifts during the load itself.

In the field, CLS covers the whole life of your page, including lazy-loaded images without dimensions, late personalised content, ads, and anything that moves after an interaction. web.dev describes this in the same article on lab and field differences.

In plain English: without field data, "CLS 0" means "no shift during a cold load of the first screen". That's all.

The lab found the cause of the slow LCP: the logo was waiting on JavaScript

After the 28 runs, I ran Lighthouse once more on the homepage and read its diagnostics instead of its score (diagnostic-home.json). Three leads came out of it.

  1. The LCP element was the logo text in the header. In the emulated 412-pixel viewport, Lighthouse picked the word "CometWeb" in the navigation bar, and the hero headline lost. The lab always reports the same element, while your visitors, as web.dev notes, may see a different one.
  2. Almost all observed LCP was render delay ("element render delay"), the time from first byte until the text appeared.
  3. Six stylesheets were blocking rendering. Lighthouse estimated they cost about 580 ms on the simulated mobile connection, with another 36 KiB of unused CSS.
Breakdown of the observed cometweb.io homepage LCP, 1.3 seconds: time to first byte 166 ms and 1,135 ms of element render delay for the logo text.
The server answered fast and the logo spent the rest of the observed LCP waiting to render, so the cause sat in the browser.Lighthouse diagnostic run, observed LCP 1.3 s, 24 Sep 2026 (diagnostic-home.json)

That's the state before any fix, and those savings are estimates from one simulated run. I tested them with the protocol's loop: one change, seven runs, compare medians.

I went after lead 3 first and deferred the CSS. Blocking stylesheets dropped from 6 to 3 and FCP improved by 0.12 seconds, but the LCP median, 6.34 seconds, stayed in the old range. Lighthouse's 580 ms estimate turned out to be too optimistic.

The real brake was JavaScript. The shared layout imported the whole glossary and a meta table for every page, so each page downloaded both before first paint. Cutting that data dropped the JavaScript loaded before first paint from 329 to 208 KB (gzipped).

With both changes live, I measured the homepage on production the same way as before. LCP dropped from 6.39 to 5.46 seconds, FCP from 4.14 to 3.64 seconds, and the score rose from 65 to 69. The table and raw runs are in the before-and-after measurement.

Median lab LCP of the homepage, 7 runs per variant: before the fix 6.39 seconds, CSS deferral alone 6.34 seconds, CSS deferral plus lighter JavaScript 5.46 seconds.
Deferring CSS barely moved LCP, and the 0.93 seconds came from slimming the JavaScript in the layout.Lighthouse 13.5, mobile profile, median of 7 runs, cometweb.io production, 24 Sep 2026

That passes step 6 of the protocol, because the new LCP median is below the fastest of the seven runs before the fix, 5.89 seconds. First content shows up 0.50 seconds sooner and the largest element on a phone's first screen 0.93 seconds sooner, in simulation for now. This page still doesn't have any field data.

An 8-step Core Web Vitals measurement protocol that survives the noise

  1. Check field data first. Note the date and whether PSI shows URL-level, origin-level or no data. When you've got both, web.dev says to let field data set your priorities.
  2. Write down the lab configuration: tool and version, device profile, throttling, cache and consent state, machine, and the benchmark index from each report.
  3. Run each version of your page at least five times. Report the median and the full range, never the best run.
  4. Read the diagnostics alongside your score: the LCP element, its phase breakdown, render-blocking requests and long tasks.
  5. Ship one focused change, so you can interpret the comparison.
  6. Compare distributions. Treat a change as real only when the new median falls outside the range the old version produced under the same configuration.
  7. Recheck field data on its own schedule. According to the CrUX API documentation, CrUX is a rolling 28-day aggregation, updated daily. Record the collection period next to every field number you report.
  8. When CrUX is empty, collect your own RUM, meaning measurements from your real visitors. The web-vitals JavaScript library is the simplest way, per web.dev's guide to measuring Web Vitals. Before you compare with CrUX, filter your RUM to Chrome. According to web.dev's explainer on CrUX and RUM differences, CrUX contains only Chrome and counts back/forward cache restores as navigations. More in the real-user monitoring glossary entry.

For a single URL, the free performance scan runs a quick lab test and an HTTP check. I cover the fixes in 7 ways to speed up your website.

Across dozens of pages, repeating this loop by hand gets old fast. The Performance module in CometWeb Insight, our audit app, keeps CrUX, lab data and its own synthetic score apart, says why data is missing, and shows the element, the request and a fix order to compare after shipping.

Final thoughts: the lab for causes, CrUX for the verdict

The easy part is running Lighthouse, and the hard part is not concluding anything from the first number.

One honest caveat: my data is a 14-point spread on four pages of one small site, plus five well-known sites. It can't settle how real people experience cometweb.io, whether other sites spread as much, or whether PSI always scores higher.

So what do you do tomorrow morning? Open PageSpeed Insights for your most important page and note whether it has field data. Then run Lighthouse five times and write down the median and the range.

That's your baseline, and to get it for your whole site at once, create a CometWeb Insight account and run your first audit. Then change one thing and measure again, the same way as the first time.

Frequently asked questions

Do Core Web Vitals affect Google rankings?

Google Search Central recommends good Core Web Vitals for success with Search and describes them as measures of real-world user experience. Neither a good lab score nor a field pass guarantees a position. Relevance, content quality, links and indexing matter as well.

How long does CrUX take to reflect a fix?

A fix shows up in CrUX gradually, as new visits replace older ones, and the whole window turns over after 28 days. Until then, the p75 mixes visits from before and after the change. Keep a repeated-run lab baseline for fast feedback.

Where else can I see CrUX data besides PageSpeed Insights?

CrUX data also appears in the Core Web Vitals report in Search Console, which runs on the same field data, and in the CrUX API. The API needs a key with CrUX access enabled. My key didn't have it for this test, which is why I used the CrUX data attached to the PSI responses.

Why does my RUM show different numbers from CrUX?

RUM and CrUX measure different sets of visits over different windows. CrUX reports the 75th percentile over a rolling 28 days, while your RUM covers whatever period you choose and usually every browser, Chrome included. Before comparing, set your RUM to the same percentile and the same 28-day window.

Sources and test data

The measurements aren't a benchmark, a field measurement of cometweb.io or a prediction for any other site.

Setup: Lighthouse 13.5.0 in Chrome for Testing 153 on an Apple M5 Pro, PageSpeed Insights API v5, CrUX data as attached to the PSI responses. Third-party sites got one PSI call each and were not crawled.

Methodology · Local lab runs · PSI responses · Summary · Diagnostic run · Lab runner · PSI caller · Analysis · Charts · Homepage fix, before and after

  1. web.dev: Web Vitals.
  2. web.dev: Why lab and field data can be different (and what to do about it).
  3. Google for Developers: About PageSpeed Insights.
  4. Chrome for Developers: CrUX methodology.
  5. Chrome for Developers: CrUX API.
  6. Lighthouse documentation: Score Variability.
  7. Lighthouse documentation: Network Throttling.
  8. Chrome for Developers: Lighthouse performance scoring.
  9. web.dev: Why is CrUX data different from my RUM data?
  10. web.dev: Getting started with measuring Web Vitals.
  11. Search Console Help: Core Web Vitals report.
  12. Google Search Central: Understanding Core Web Vitals and Google search results.

Check your site in Insight

Free audit for performance, SEO, accessibility and security — with clear priorities.

No credit card Report with date and sources Free account in the app