CrUX field percentiles often look worse than a green Lighthouse run on the same URL. Here is why the gap is normal, what moves real-user Core Web Vitals, and what not to chase.
On Monday the Lighthouse performance score on checkout was 94. Mobile lab LCP sat under 2.5 seconds and CLS looked clean. On Wednesday someone opened PageSpeed Insights field data for the same URL and Chrome UX Report still showed Needs improvement on mobile, with LCP p75 above the Good threshold.
That pattern is not a broken tool. It is the ordinary gap between lab and field on a URL that real visitors use under conditions your Lighthouse run never reproduced. The question behind the screenshot is usually two-part: why is CrUX worse than Lighthouse, and what can you change this sprint that will actually move field percentiles instead of chasing a single green lab run?
Why does Chrome UX Report look worse than Lighthouse on the same URL?
Lighthouse, including the lab section in PageSpeed Insights, measures a controlled load: fixed device profile, emulated network, cold or warm cache depending on settings, and one navigation path you chose for the audit. Chrome UX Report (CrUX) aggregates real Chrome sessions over a rolling window (currently 28 days for the public dataset). Field LCP, INP, and CLS reflect actual devices, networks, cache states, scroll depth, and interaction paths across thousands of visits, not one scripted run.
Google's lab versus field data differences article states the split plainly: Lighthouse returns one LCP candidate for a given run, while field LCP on the same URL often spans several elements depending on viewport and how far the user scrolled. A hero image that wins LCP in lab may lose to a product grid on a shorter phone, or never become the LCP element if most users bounce before it paints.
CrUX also reports percentiles (typically p75 for Core Web Vitals), not your best session. A Lighthouse performance score near 100 describes one favourable run on one profile; CrUX describes what most visitors experienced across the rolling window. If even a quarter of sessions on mid-range Android hardware hit slow LCP, the p75 can sit in Needs improvement while lab looks green on a desktop profile someone ran by mistake.
| Signal | What it measures | Typical gap when CrUX looks worse |
| Lighthouse (lab) | One controlled run on a URL you choose | Best-case load on a fixed device and network profile |
| CrUX (field) | 28-day p75 from real Chrome sessions | Slower devices, mixed cache states, full interaction paths |
| PageSpeed Insights | Shows both on one page | Easy to compare the wrong rows (desktop lab vs mobile field) |
How do device mix, cache warmth, and the 28-day window widen the CrUX versus Lighthouse gap?
Three mechanics explain most tickets where CrUX looks much worse than Lighthouse without a deploy bug.
Device and network skew widens lab versus field
Lab uses a defined mobile or desktop strategy. CrUX includes every eligible phone and desktop session that visited the URL during the collection period. If your traffic skews toward older Android handsets on congested mobile networks, field LCP and INP will punish layouts that a Moto G emulation in Lighthouse barely stresses. PageSpeed Insights already splits mobile and desktop field rows; compare like with like before you conclude the tools disagree.
Cache warmth and return visits change who looks fast
Lighthouse often runs as a cold or lightly cached load. Returning visitors with warm HTTP cache, service worker caches, or prerendered shells can look faster in field for LCP while first-time visitors still struggle. The opposite happens too: lab on a fast connection hides consent banners and personalisation layers that only appear for logged-in or European traffic, which field data must include.
The 28-day rolling average lags your latest deploy
CrUX is not yesterday's score. Sessions from before your fix remain in the window until they age out, so field percentiles improve gradually even when today's lab runs are green. We unpacked that lag on the Watcher blog in Why Your Core Web Vitals Fix Isn't in CrUX Yet (28-Day Window). If you shipped last week, amber CrUX beside green Lighthouse may mean the average has not caught up yet, not that the optimisation failed.
What changes actually improve CrUX Core Web Vitals percentiles?
Field data moves when enough real sessions experience better LCP, INP, or CLS. Lab work still matters because it tells you what to ship and gives you a reproducible before-and-after on the same URL list; field tells you whether enough users received the benefit once those sessions enter the CrUX window.
- Serve the LCP image or text block from the same origin when possible; cut redirect chains on the hero asset.
- Size and compress the true LCP element for the viewport widths that dominate analytics, not only the designer's reference frame.
- Reserve space for heroes, carousels, and video posters so late-loading assets do not reshuffle the LCP candidate between sessions.
- Reduce render-blocking CSS and fonts on the template that owns the money URL, not only the marketing homepage.
INP levers on flows CrUX actually records
INP replaced FID as a Core Web Vital in 2024. Field INP punishes main-thread work during interaction: heavy JavaScript on product configurators, map panes, autocomplete, and checkout steps. Defer non-critical scripts until after first interaction, split long tasks, and test on mid-tier hardware with CPU throttling. A green lab TBT does not clear a bad field INP if users tap through a data grid built for desktop.
CLS levers that field sessions notice early
Reserve dimensions for ads, embeds, cookie banners, and dynamic promo slots before they inject. Late consent or A/B widgets are a common reason lab CLS looks fine while field CLS fails: the lab run never hit the variant your European traffic sees every day. After each change, keep scheduled lab runs on the same URL list and watch CrUX weekly; expect gradual movement, not a next-day flip. For how lab schedules and field snapshots fit together in a monitoring stack, see When to Use Synthetic vs Real User Monitoring for Performance.
What should you not do when trying to improve CrUX scores?
Some reported fixes optimise the Lighthouse screenshot, not the visitor experience that CrUX aggregates.
- Do not treat one Lighthouse run as proof CrUX should already be green. Lab confirms the deploy; CrUX confirms the rolling aggregate moved.
- Do not rerun Lighthouse until you get 100 and close the ticket. Without stored history you cannot show whether field-relevant templates changed.
- Do not compare desktop lab to mobile CrUX (or the reverse) and call it a tooling bug.
- Do not strip analytics or consent in lab only. If field traffic still loads those tags, CrUX will keep reporting the slower experience.
- Do not expect origin-level field data to excuse a bad conversion URL. When PageSpeed Insights falls back to origin CrUX, a healthy homepage can mask a failing checkout template until URL-level samples exist.
Lab green, field amber: reconciling two PageSpeed truths covers the weekly reconciliation habit when both signals disagree. Use that piece for ongoing triage; use the checklist below when you need a one-pass improve list.
FAQ
Is CrUX more accurate than Lighthouse?
They measure different things. Lighthouse is accurate for repeatable lab diagnosis on a URL you choose. CrUX is accurate for how Chrome users experienced Core Web Vitals across the rolling window. For Search and page-experience reporting, treat CrUX (and Search Console URL groups fed by the same field data) as the public field reference when samples exist.
How long after a fix before CrUX improves?
There is no single flip day. High-traffic URLs often show movement within one to three weeks as post-fix sessions replace older ones inside the 28-day window. Low-traffic templates can take longer or never earn stable URL-level CrUX; use scheduled lab proof until samples appear.
Can I improve CrUX without real user monitoring?
Yes, by shipping changes that lab identifies on priority URLs and waiting for enough Chrome traffic to reflect them in CrUX. First-party RUM adds faster, segmented feedback if you already run a snippet, but CrUX improvement does not require buying RUM to start. It does require patience with the rolling average.
Why does PageSpeed Insights show different numbers than Search Console?
Both draw on CrUX for field Core Web Vitals, but grouping, thresholds, and UI differ. Align on URL, form factor, and date range before you argue the products disagree. Lab sections in PageSpeed Insights will always diverge from field rows by design.
How do you improve CrUX when Lighthouse already looks green?
- Open PageSpeed Insights for the URL on mobile field data. Note LCP, INP, and CLS p75 and whether URL-level or origin-level samples apply.
- Run a matching mobile lab audit on the same URL. Record which element Lighthouse reports as LCP and whether INP interaction was exercised.
- List differences lab skipped: consent, personalisation, third-party tags, logged-in states, geographic variants.
- Ship one field-relevant fix (LCP asset, INP task split, CLS reservation). Keep lab history on a schedule.
- Re-check CrUX weekly with the collection period dates visible. Tell stakeholders field lags lab by design.
If step 5 is missing, you are optimising for a Lighthouse screenshot while CrUX still averages three weeks of older sessions. Pair the checklist with stored lab history so the next client question is a trend line, not two isolated PageSpeed Insights pastes.
What to read next when CrUX still trails Lighthouse
CrUX worse than Lighthouse is usually a population mismatch, a percentile versus single-run mismatch, or a rolling window that has not absorbed your fix yet. Use lab to decide what to ship; use field to learn whether enough real sessions improved. Read Why Your Core Web Vitals Fix Isn't in CrUX Yet for the timeline mechanics, and synthetic versus real user monitoring when you are choosing how much lab history to store beside CrUX. Improve the visitor experience first; the public percentile follows when traffic proves it.
References
Originally published on Hashnode.