Why Your Lab LCP Says 1.8s and Your Field p75 Says 3.4s
This page extends Understanding Core Web Vitals Thresholds within Core Web Vitals & Measurement. A synthetic run reports a comfortable 1.8s, well inside the 2.5s boundary. Field data reports a p75 of 3.4s. The instinct is to distrust one of them, and that is the wrong move — a lab run is a single sample from a distribution, taken under conditions you chose. The p75 is a property of the distribution.
Understanding the six differences turns a contradiction into a diagnostic.
Rapid Diagnosis: Which Difference Dominates?
- Look at the device split. If your field mix is 70% mobile and your lab profile is a desktop, the CPU difference alone can double render delay.
- Check the LCP element per segment. Field data may show a different element winning on mobile than the one Lighthouse reported on desktop — different element, different bottleneck.
- Compare cache states. Lab runs are cold by default; a large share of real visits are warm. This usually pushes the field number down, so if field is still worse, the cold-visit tail is severe.
- Check the network shape, not just the speed. Lighthouse's simulated throttling models a clean link; real mobile connections have variable latency, packet loss and radio wake-up delays that no simulation reproduces.
Root Cause Analysis: The Six Differences
1. Device CPU. A lab run on a development machine has several times the single-core performance of a mid-tier Android phone. Since render delay is CPU-bound, that difference alone can add a second on the same page.
2. Cache state. Synthetic runs start cold. Real traffic is a mixture, and the two populations behave so differently that a single aggregate hides both. Always segment field LCP by cache state before comparing.
3. A different LCP element. The largest element in a 1350px-wide desktop viewport is often not the largest in a 390px mobile one. If the element differs, the two numbers are measuring different resources entirely.
4. Network model. Simulated throttling applies a fixed round-trip time and bandwidth cap to a clean connection. Real mobile links have variable latency, occasional loss and radio state transitions, which are consistently worse than the model.
5. Viewport and above-the-fold content. A tall desktop viewport can include an image that is below the fold on mobile — and off-screen elements never become LCP candidates.
6. Third parties that behave differently. Consent managers, personalisation and A/B tools frequently short-circuit for a headless run or a lab user agent, so the synthetic page is simply not the page users get.
Step-by-Step Resolution: Make the Two Numbers Comparable
1. Match the lab profile to the slow quartile, not to the average.
npx lighthouse https://example.com --quiet --output=json \
--form-factor=mobile --screenEmulation.mobile \
--throttling.cpuSlowdownMultiplier=4 \
--throttling.rttMs=170 --throttling.throughputKbps=1600
# trade-off: a harsher profile makes every run look worse and can demoralise a
# team. It is still the right default — a profile that only reproduces your
# fastest users cannot predict the metric that is actually assessed.
Expected outcome: the lab number moves toward the field p75, usually closing half the gap immediately.
2. Confirm the LCP element matches. Read the element from field attribution and from the lab run; if they differ, fix the comparison before fixing the page.
new PerformanceObserver((l) => {
const e = l.getEntries().at(-1);
console.log('LCP element', e.element?.tagName, e.element?.className,
'at', Math.round(e.startTime));
}).observe({ type: 'largest-contentful-paint', buffered: true });
3. Segment the field number before drawing conclusions. Split by device class, cache state and connection type. A single p75 that fails often decomposes into three passing segments and one badly failing one — and that one is the work.
SELECT device_class, cache_state,
approx_quantile(value, 0.75) AS p75, count(*) AS n
FROM rum_events WHERE name = 'LCP' AND ts > now() - interval '7 days'
GROUP BY 1, 2 ORDER BY p75 DESC;
-- trade-off: more dimensions means smaller groups and noisier percentiles.
-- Keep segments above roughly a thousand samples a day or the ranking will
-- reorder itself daily for no real reason.
4. Fix for the failing segment, then re-verify in both places. A change that improves the lab number but not the failing segment's field number was the wrong change, and only the segmentation would have told you.
Verification
After a fix, expect the two measurements to move differently and in a predictable way: the lab number moves immediately and by a known amount; the field p75 moves over days, by less, and only in proportion to how much of your traffic resembles the profile you fixed.
That asymmetry is the signal to watch. If the lab improves and the field does not move at all after a full reporting window, the change addressed a condition your users do not experience — a common outcome of optimising against a fast desktop profile.
Keep both signals in CI and in the dashboard, with the relationship documented. The pairing to use: lab assertions as a pre-merge gate, described in the best Lighthouse CI setup for frontend pipelines, and a field alert on relative regression per route from your RUM pipeline.
FAQ
Which number should I optimise against?
The field p75, always — it is what the assessment uses and what users experience. The lab is for diagnosis: it tells you why a page is slow with a repeatable trace you can profile. Use the field number to choose what to work on and to confirm the result; use the lab to find the mechanism in between.
Why is my field LCP better than my lab LCP?
Usually cache warmth. Repeat visitors skip most of the load entirely, so a field aggregate dominated by returning users can beat a cold synthetic run comfortably. That is not necessarily good news: the first-visit segment may still be failing badly and is being averaged away. Segment by cache state before celebrating.
Does Lighthouse's simulated throttling understate real-world slowness?
Generally yes. Simulation applies a clean latency and bandwidth model, while real mobile networks add variable latency, retransmissions and radio wake-up costs. Using applied throttling — actually slowing the connection rather than modelling it — reproduces field conditions more closely at the cost of longer, noisier runs. Simulated is the right default for CI; applied is better for a one-off investigation of a stubborn gap.
How many synthetic runs do I need for a stable number?
At least three, and take the median rather than the mean. A single Lighthouse run varies by a few hundred milliseconds on the same page and machine, which is enough to make an unchanged deploy look like a regression. Three runs and a median is the usual compromise between stability and CI time; five is worth it for a release gate. Never compare a one-run number against a one-run number and treat the difference as signal.
Should CI fail on the lab number if the field number is what matters?
Yes, because the lab number is the only one available before the code ships. Treat it as a pre-merge guard against known-bad patterns — a render-blocking resource appearing, a bundle crossing a budget — rather than as a prediction of the field p75. The field alert is the outcome measure; the lab assertion is the mechanism that stops obvious regressions from reaching it.
Why did my field p75 move without any deploy?
Because the population changed, not the page. A marketing campaign that brings in slower devices, a seasonal shift in geography, a change in the mix of new versus returning visitors, or a third-party tag updating itself will all move the number with your code untouched. Before investigating the page, check whether the distribution of device class, country and cache state changed over the same window — that comparison usually answers it in a minute.
Related
- Understanding Core Web Vitals Thresholds — what the p75 boundary means and why it is assessed that way.
- CrUX vs your own RUM — the same reconciliation between two field sources.
- Measuring LCP with Chrome DevTools — the phase breakdown that explains which part of the gap is CPU and which is network.