You shipped the LCP fix three weeks ago. Your own RUM shows the improvement clearly. PageSpeed Insights still reports almost the same number, and Search Console still has the URL group in "Needs improvement." Nothing is broken. You are reading a statistic that was designed to be slow.
The Chrome User Experience Report is not a measurement of your site's performance today. It is the 75th percentile of a specific metric, computed over a rolling 28-day window, across qualifying page loads from a subset of Chrome users. Every surprising thing about CrUX data falls out of that sentence.
What CrUX actually samples
CrUX collects field data from Chrome users who have opted into usage statistic reporting and meet Google's eligibility conditions. That has three consequences people routinely forget:
- It is Chrome-only. If a large share of your mobile traffic is iOS Safari, those sessions are not represented. Your own RUM — which usually instruments every browser — will diverge from CrUX structurally, not because one of them is wrong.
- It only covers publicly discoverable URLs with enough traffic. Google documents that origins and URLs need sufficient samples to appear. Logged-in pages, thin-traffic pages, and most long-tail URLs will never have URL-level data.
- It is page-view weighted. Origin-level numbers are dominated by your highest-traffic templates. A slow but rarely visited section barely registers; a fast homepage can mask everything behind it.
So CrUX is not "the truth" against which your RUM is checked. It is a different population, sampled differently, and aggregated differently.
Why the rolling window makes every fix look slow
The CrUX API and the PageSpeed Insights field section report a trailing 28-day aggregate. On the day you deploy, the window contains 28 days of pre-fix page loads. Each subsequent day swaps one day of old data out for one day of new data.
If your traffic is roughly even day to day, the share of the window reflecting the new code looks like this:
| Days since deploy | Share of window with new code | What you'll observe |
|---|---|---|
| 3 | ~11% | Essentially no movement |
| 7 | ~25% | A nudge, easily mistaken for noise |
| 14 | ~50% | Clear direction, wrong magnitude |
| 28 | 100% | The real number, finally |
This is a step response, not a lag. The reading moves continuously toward the new value and only arrives at day 28. Judging the size of an improvement at day 10 will understate it by roughly half. Judging a regression at day 10 does the same, which is worse — a serious regression looks survivable for two weeks.
The monthly BigQuery dataset adds a second layer: it aggregates a calendar month and is published in the following month. If you need CrUX for a deploy decision, use the daily-updating CrUX API, not the BigQuery tables.
The CrUX History API softens this by giving you a series of weekly datapoints, but each datapoint is still its own trailing 28-day window. Consecutive points share 21 days of data. They are heavily autocorrelated by construction, which is why history charts look smooth even when the underlying week-to-week experience is jumpy. Do not treat two adjacent points as independent observations.
Why the 75th percentile doesn't move like an average
Core Web Vitals assessment uses the 75th percentile of each metric: LCP at or under 2.5s, INP at or under 200ms, CLS at or under 0.1. A p75 is a boundary, and boundaries move only when density crosses them.
This produces two counterintuitive behaviors:
- Making fast loads faster does nothing. If you cut 400ms off your cached, warm-connection sessions, your mean improves and your p75 does not budge. The user at the 75th percentile was never in that group.
- Small changes near the threshold produce large swings. If your distribution has a dense cluster of page loads at 2.4–2.6s, a 100ms shift flips a large share of sessions across the line, and the reported p75 jumps discontinuously.
The practical implication: look at the bin densities, not just the headline p75. The CrUX API returns good/needs-improvement/poor densities alongside the percentile. A fix that moves 8% of loads out of "poor" is real progress even if p75 is flat — and it tells you where the next fix should land.
Why PageSpeed Insights sometimes shows data for a page you never measured
When a URL lacks sufficient samples, PSI falls back to origin-level field data and labels it as such. Teams skim past the label and conclude their new landing page is slow, when they are looking at a site-wide aggregate dominated by unrelated templates.
You can make the fallback explicit by querying the CrUX API directly for the URL. If the URL has no record, the API returns a not-found response instead of silently substituting the origin:
POST https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=API_KEY
{
"url": "https://example.com/product/widget",
"formFactor": "PHONE"
}
If that 404s and the origin query succeeds, you now know the page has no field data at all — which is itself the finding. Lab tools are your only option for that URL until traffic grows.
How mix shifts move your numbers with no code change
CrUX reports can be segmented by form factor. The unsegmented number is a blend, so the blend's composition matters.
If a seasonal campaign shifts your traffic from 40% phone to 60% phone, and phone p75 LCP is worse than desktop, your overall p75 degrades without a single line of code changing. The same happens with geography — a push into a market with slower networks drags the aggregate — and with page mix, when a slow template's share of total page views rises.
This is the same arithmetic as a Simpson's paradox: every segment can improve while the total gets worse. Before you investigate a CrUX regression as an engineering problem, pull the phone and desktop series separately and check whether their individual p75s actually moved.
What to do with this
- Timestamp your deploys in your own RUM. First-party field data gives you per-day resolution and arbitrary percentiles. Use it to decide whether a fix worked; use CrUX to confirm how Google scores it.
- Set a 28-day verification date when you ship a performance change, and don't declare success or failure before it.
- Track bin densities alongside p75. Movement in the poor bucket is the leading indicator; p75 is the lagging scoreboard.
- Always segment by form factor before diagnosing a change, and check whether traffic mix moved.
- Check whether PSI is showing URL-level or origin-level data before acting on a single page's numbers.
- Treat CrUX History points as overlapping windows. Compare points at least four weeks apart if you want something close to independent samples.