Finding the Cause of a Sudden Field LCP Drop
The RUM dashboard shows it on a Monday morning: mobile LCP at the 75th percentile went from 1.8 seconds to 2.7 over the weekend. Nobody deployed. Or somebody did, but the change was a typo fix. Sudden field regressions on static sites are common precisely because so much that affects LCP is not in the repository — third-party scripts, CDN behaviour, consent tools, traffic sources — and because field data blends all of it. Finding the cause is a matter of slicing the data until one segment explains the change, then confirming it in the lab.
This guide works through one such investigation on a 3,000-page Astro marketing and documentation site, which turned out to have two overlapping causes. It shows the RUM fields that make diagnosis possible, the order of questions to ask, and how each candidate was confirmed or ruled out. It belongs to Monitoring Static Sites in Production; the dashboard itself is built in Building a Core Web Vitals Dashboard from RUM Data.
What the RUM Data Needs to Contain
Diagnosis depends on what was recorded. The site's web-vitals beacon sends, for every page view:
onLCP(({ value, attribution: a }) => send({
metric: 'LCP', value, page: location.pathname, template: document.body.dataset.template,
element: a.element, url: a.url, // the LCP element and its resource
ttfb: a.timeToFirstByte, loadDelay: a.resourceLoadDelay,
loadTime: a.resourceLoadDuration, renderDelay: a.elementRenderDelay,
device: navigator.userAgentData?.mobile ? 'mobile' : 'desktop',
conn: navigator.connection?.effectiveType, country: cfCountry, ref: document.referrer && new URL(document.referrer).host,
build: document.querySelector('meta[name=build-id]')?.content,
}));
The four LCP sub-parts are the most useful fields: they say whether the time went into getting HTML, starting the LCP resource, downloading it, or rendering it.
Step 1: Pin Down When and Where
The p75 by hour showed the step change starting Saturday at about 14:00 UTC, not gradually. By template, it was not uniform:
| Template | p75 before | p75 after | Change |
|---|---|---|---|
| Landing pages | 1.9 s | 3.4 s | +1.5 s |
| Docs articles | 1.7 s | 2.0 s | +0.3 s |
| Blog posts | 1.8 s | 1.9 s | +0.1 s |
A step change at one time, concentrated on one template: something changed on Saturday afternoon that affected landing pages most.
Step 2: Line It Up With Changes
A timeline of everything that changed around Saturday 14:00, from every source the team could think of:
| Time (UTC) | Change | Source |
|---|---|---|
| Fri 16:20 | Deploy: typo fixes in 3 docs pages | Deploy log |
| Sat 13:55 | Marketing published a new paid social campaign to landing pages | Ad platform |
| Sat 14:10 | Tag manager container version 47 published | Tag manager history |
| Sun 09:00 | Chrome stable update rolled out | Browser release notes |
The Friday deploy predated the change and touched no landing pages. The campaign and the tag manager publish were 15 minutes apart, right at the step. Two suspects.
Step 3: Use the Sub-Parts to Separate the Suspects
LCP sub-parts for landing pages, before and after:
| Sub-part (p75, mobile) | Before | After |
|---|---|---|
| Time to first byte | 320 ms | 410 ms |
| Resource load delay | 180 ms | 640 ms |
| Resource load duration | 520 ms | 790 ms |
| Element render delay | 140 ms | 380 ms |
Every phase got worse, which suggested two things happening at once. Load delay and render delay — time before the hero image request started, and time between its arrival and paint — point at something competing on the main thread and network early in the load. That fits a tag manager change. Higher TTFB and download time point at slower networks, which fits a traffic change.
Step 4: Check the Traffic Mix
Segmenting by referrer settled the first suspect. The new campaign sent 41% of landing page traffic over the weekend, from a social network's in-app browser, mostly on lower-end Android devices with effectiveType of 3g or slow-4g. Restricting the comparison to visitors who did not come from the campaign:
| Landing pages, excluding campaign | Before | After |
|---|---|---|
| LCP p75 | 1.9 s | 2.4 s |
So the traffic mix explained about two-thirds of the regression on landing pages — readers on slower devices and networks — and something else explained the remaining 0.5 seconds for everyone.
Step 5: Confirm the Tag Manager Change in the Lab
The tag manager diff between versions 46 and 47 showed one new tag: a heatmap and session-recording script, set to fire on "Page View – DOM Ready" on all landing pages, synchronously injected. WebPageTest on a landing page with a Moto G Power profile, run against the live site with the new tag blocked via WebPageTest's block setting and then unblocked:
| Lab, landing page | Tag blocked | Tag active |
|---|---|---|
| LCP | 1.8 s | 2.4 s |
| Main-thread time before LCP | 210 ms | 690 ms |
| Requests before LCP | 14 | 23 |
That reproduced the remaining half-second. The heatmap tag moved to fire after the window load event and only on a 10% sample of sessions, which marketing agreed was enough for its purpose.
Step 6: Decide What "Fixed" Means
After the tag change, landing page LCP for non-campaign visitors returned to 1.9 seconds. Overall landing page p75 stayed higher, at 2.6 seconds, while the campaign ran — not a regression in the site but a change in who was visiting. The team made two follow-ups: a lighter landing page variant for the campaign's in-app traffic (smaller hero, no video), which brought campaign visitors' LCP from 3.9 to 2.8 seconds, and dashboard segments by referrer so the next campaign is visible as a traffic change at a glance.
Measured Impact
| Measure (mobile, landing pages) | Weekend after | After fixes | Tool |
|---|---|---|---|
| LCP p75, all visitors | 3.4 s | 2.6 s | web-vitals RUM |
| LCP p75, excluding campaign | 2.4 s | 1.9 s | web-vitals RUM |
| LCP p75, campaign visitors | 3.9 s | 2.8 s (lighter variant) | web-vitals RUM |
| Time from alert to cause identified | — | 3 h 20 min | Incident notes |
The tag manager now requires a second approver for container publishes that add tags to landing pages.
Pitfalls & Rollback
- Looking only at deploys. Tag managers, campaigns and CDNs change without them.
- No LCP sub-parts in RUM. Without them, every cause looks the same.
- Treating a traffic shift as a regression. Segment by referrer and device first.
- Relying on CrUX for speed. Its 28-day window hides sudden changes for weeks.
- Rollback: tag manager versions can be reverted in minutes; that is the fastest test of a tag-related suspect in production.
Conclusion
A sudden field LCP drop on a static site is often not in the site's code. Here it was two overlapping causes: a campaign that brought slower devices and networks, and a heatmap tag published in the tag manager on the same afternoon. RUM with LCP sub-parts, a timeline of every change from every source, segmentation by traffic, and a lab test with the suspect blocked separated them in an afternoon.
FAQ
Why did my field LCP get worse when nothing was deployed?
Field LCP depends on more than your code. Third-party scripts change without a deploy, CDN configuration or cache behaviour can change, traffic can shift to slower devices or regions through a campaign, and browsers update. Segmenting field data by page template, device, country and referrer usually shows which of these it is.
How quickly does CrUX show an LCP regression?
The public CrUX dataset is a rolling 28-day window, so a sudden regression appears gradually and takes up to four weeks to show fully, and the monthly BigQuery data lags further. Your own RUM data with the web-vitals library shows a change within hours, which is why it is worth collecting even if you also watch CrUX.
What should real user monitoring record to diagnose LCP?
The LCP value and element, the LCP sub-parts from the attribution build of web-vitals, which are time to first byte, resource load delay, resource load duration and element render delay, plus page template, device class, connection type, country and a build identifier. The sub-parts point at the phase that got slower, which narrows the cause quickly.
Can a lab test reproduce a field LCP regression?
Often, once you know which pages and conditions are affected. Run WebPageTest or Lighthouse on an affected template with a matching device and connection profile, before and after the suspected change, and compare filmstrips and waterfalls. If the lab does not reproduce it, the cause is probably in traffic mix or real network conditions rather than the page.
Related
- Parent: Monitoring Static Sites in Production — watching a live static site.
- Building a Core Web Vitals Dashboard from RUM Data — the data used here.
- Tracking Deploy Markers in Analytics — putting changes on the timeline.
- Measuring LCP Subparts with DevTools — the sub-parts in the lab.
- Loading Google Tag Manager Without Hurting INP — keeping tags out of the critical path.
Filed underFixes