Registered protocol · adoption-cohort design

Do WordPress speed plugins work in the field?

WP Rocket, NitroPack and LiteSpeed Cache promise to fix Core Web Vitals. Almost all the evidence offered for them is a synthetic Lighthouse score or a cross-sectional dashboard — and neither can tell you what happens to real users after a site installs one. This is the design that can: a staggered adoption-cohort event study linking HTTP Archive plugin markers to Chrome UX Report field outcomes. We publish the full protocol and a runnable replication package — and we do not fabricate the effect estimates.

Status: a registered protocol, not a results paper

This article specifies the experiment in full — detection, panel, estimator, falsification tests and decision rules — and ships the SQL, R and Python to run it against public BigQuery data. It contains no treatment-effect numbers, because the only honest way to state them is to extract the origin-month panel first. Every figure below is an explicitly-labelled design schematic. When the extraction is run, the results drop into the same tables and figures with their methodology already public — the strongest form of pre-commitment we can offer.

The question the market can't answer

WordPress speed plugins make genuine engineering interventions: full-page caching, minification, critical-CSS generation, unused-CSS removal, lazy loading, CDN integration, image optimisation, font optimisation, script deferral, and delaying JavaScript until the first user interaction. NitroPack advertises caching, image and code optimisation, an integrated CDN and lazy loading; LiteSpeed Cache separates page-optimisation features from cache features that depend on a LiteSpeed server or QUIC.cloud; WP Rocket documents delay-JavaScript as holding script execution until interaction, tagging delayed scripts with type="text/rocketlazyloadscript" and replacing src with data-rocket-src.

Those interventions land in two different measurement regimes, and the regimes can disagree. PageSpeed Insights reports both: lab diagnostics from Lighthouse, collected in a controlled synthetic environment, and field data from CrUX — the real experience of Chrome users over a trailing 28-day window. Google's own documentation says the lab number is for debugging and may not capture real-world bottlenecks, while the field number is the true user experience. A plugin can therefore improve Lighthouse audits and still fail to move what users feel — because real visitors arrive on different devices and networks, with warm caches, from different countries, on product and checkout pages rather than the home page, through consent banners, ads and personalisation the lab never sees.

So any honest claim about a plugin has to survive three separate layers — and most marketing collapses them into one:

Three stacked layers a claim must survive. Layer 1, mechanism: delay-JS removes initial main-thread work (passes). Layer 2, lab measurement: Lighthouse Total Blocking Time drops and the PageSpeed score rises (passes). Layer 3, the field outcome from CrUX real users — the gate that matters — can split: LCP good-share may rise while INP good-share may fall.
Figure 1 — The three layers of speed-plugin evidence, with delay-JavaScript as the worked example. A plugin can pass the mechanism and lab layers and still fail the only one users feel. Illustrative.
3layers a claim must survive — mechanism, lab, field
4treatments: WP Rocket, NitroPack, LiteSpeed, delay-JS
−6…+12event-time window, in months around adoption
0fabricated effect estimates in this release

Why the public evidence isn't enough

  • Cross-sectional dashboards aren't causal. HTTP Archive's Core Web Vitals Technology Report is a valuable public resource — it joins CrUX field metrics to HTTP Archive technology detections. But it compares sites that use a plugin to sites that don't, and those groups differ in ways that move CrUX on their own: plugin users are more performance-aware, more likely to pay for good hosting, run WooCommerce, use heavy page builders, or sell in ad- and consent-heavy verticals. A pass-rate gap can't separate the plugin from the company it keeps. The defensible question is not “do plugin users have better CWV?” but “what happened to the same sites after they adopted, versus comparable sites that didn't?”
  • A lab-score uplift is a mechanism signal, not a field outcome. PageSpeed Insights reports lab and field precisely because they can diverge. A plugin can cut Total Blocking Time in a synthetic test by deferring scripts, while CrUX INP — measured across real interactions — does not improve, or regresses. The remedy isn't to adjudicate vendor disputes in prose; it's to make the field metric the primary endpoint and treat Lighthouse as a falsifiable secondary.
  • WordPress Core already proved the pattern — for itself, not for plugins. The WordPress performance team has used exactly this HTTP Archive × CrUX field method to validate core releases (the WordPress 6.3 field analysis states plainly that lab benchmarks aren't representative of the average production site, and the 2024 field-research guide lays out the BigQuery workflow). The same before/after logic has never been applied systematically to commercial speed plugins — nor to delay-JavaScript after INP became a Core Web Vital in March 2024.

The gap, then, is specific and closable: the data exists, the field method is proven, and the only missing piece is a design that turns plugin adoption into a treatment event.

The design in one picture

Plugin installs are natural experiments staggered across calendar time: different origins adopt in different months. That lets us build an event study. For each treated origin we line up its CrUX outcomes by months-since-adoption, compare against matched origins that have not yet adopted, and read the treatment effect as a function of event time k. The signature of a real win is unmistakable — and so is its absence.

An event-study plot of treatment effect against event time from minus six to plus twelve months. Before adoption all curves sit on zero. After adoption three illustrative outcomes diverge: a field-positive curve rises and persists above zero; a lab-only curve stays flat on zero; a field-negative curve, the delay-JavaScript INP risk, falls below zero.
Figure 2 — The estimand, drawn. Flat pre-adoption “leads” are the testable assumption that adopters weren't already improving; the post-adoption shape is what the study estimates. A field-positive plugin rises and persists; a lab-only one never leaves zero; delay-JS may even push INP negative. Illustrative schematic — no effect is claimed here.

Formally, for plugin or feature p, adoption cohort g (the month a group of origins first adopts) and event time k, the primary estimand is the group-time average treatment effect on the treated:

ATT_p(g, k) = E[ Y_i,g+k(1) − Y_i,g+k(0) | G_i = g, T_i = p ]

Y         a CrUX field outcome at the origin-month-device level
G_i       origin i's first validated adoption month
Y(1),Y(0) potential outcomes with / without the plugin; the untreated
          path is estimated from matched not-yet-treated WordPress origins

The whole credibility of the answer rests on the left half of that picture. If the leads are flat — adopters look just like their controls in the six months before they install — then the jump after adoption is plausibly the plugin. If the leads already slope up, the “effect” is just performance-aware owners who were improving anyway, and the design honestly reports that the effect is not identified.

Research question & hypotheses

Question. Do WP Rocket, NitroPack, LiteSpeed Cache and delay-JavaScript-until-interaction improve CrUX field metrics after adoption, relative to matched WordPress origins that have not yet adopted the same treatment? The hypotheses are pre-committed and directional:

  1. H1 · LCP responds most

    Treatments that cut server response time, transfer size or render-blocking resources improve mobile LCP and the good-LCP share after adoption — with larger effects on origins that were poor or borderline beforehand. Loading-time mechanisms map most directly onto LCP.

  2. H2 · INP responds least, and least predictably

    Effects on INP are weaker and more heterogeneous than on LCP, because INP is set by real interactions, event handlers, long tasks, DOM size, third parties and client-side rendering — not by initial load. A caching plugin barely touches most of those.

  3. H3 · Delay-JS is the ambiguous case

    Delaying JavaScript until interaction reduces initial main-thread work and flatters lab diagnostics, but can worsen the first interaction when above-the-fold scripts are deferred. WP Rocket's own documentation warns delay-JS “can potentially have a negative effect on INP” and that scripts needed for critical interactions should be excluded. This is the cleanest test of the lab-versus-field divide on the page.

  4. H4 · Gains are conditional, not universal

    Effects vary by hosting, baseline rank, theme/page-builder stack, ad and analytics load, e-commerce status and geography. A plugin that rescues a bloated page on weak shared hosting may do almost nothing for an already-lean origin — so a single global verdict would be dishonest.

Data architecture

The analysis dataset is an origin-month panel built entirely from public BigQuery tables — no private crawl, no vendor data. HTTP Archive supplies the technology and resource side: httparchive.crawl.pages carries monthly page rows with rank, page summaries, Lighthouse data and Wappalyzer-detected technologies, and httparchive.crawl.requests carries one row per request, including response headers and text response bodies — the raw material for feature-level markers. CrUX supplies the field side: its monthly materialised tables (device_summary by month, origin and device; country_summary for geography) expose good/needs-improvement/poor fractions and approximate p75 values per origin — exactly the shape an origin-month event study needs.

The subtlety that wrecks naïve before/after comparisons is timing. An HTTP Archive crawl is a single page-load snapshot; a CrUX month is a trailing 28-day field window. The two don't line up, so the crawl month is treated as ambiguous rather than as “post”:

A five-month timeline. The plugin goes live during month m. HTTP Archive detects the marker in a single snapshot in month m. CrUX month m-1 is a 28-day window fully before adoption (baseline); CrUX month m straddles the switch and mixes pre- and post-adoption users, so it is ambiguous and omitted or down-weighted; CrUX month m+1 is fully after adoption and is the first clean field outcome.
Figure 3 — Why CrUX month m can't be the post-period. Its 28-day window straddles the install, mixing pre- and post-adoption users. The primary post-period starts at m+1; month m is dropped or down-weighted as a transition.

A worked example makes the rule concrete. Suppose an origin first shows the WP Rocket delay-JS markers in the June crawl. June's CrUX entry summarises real users across roughly the trailing 28 days — many of whom visited before the plugin went live. Reading June as “after” would dilute a real effect toward zero (or smear a regression). So June is the transition month, and the first outcome we trust is July (m+1). The pre-period — the placebo leads — is the spring months, which must look flat.

Form factor. Mobile/phone is the primary surface: it's the stricter performance environment, the one these plugins are marketed around, and the one HTTP Archive (mobile crawl) and CrUX (phone dimension) both cover cleanly. Desktop is secondary.

Treatment detection — and the persistence rule

A treatment is an origin-level monthly state, not a one-off page attribute. A site counts as using plugin p in month m if Wappalyzer detects the technology, or raw HTML/header markers satisfy the plugin-specific rule. Crucially, an adoption event requires persistence — the marker must appear and stay — which strips out crawl noise, cache variance, A/B experiments and detection artefacts:

A four-month state strip. A valid adoption event is absent in months m-2 and m-1, then present in m and m+1. A valid removal event is the mirror: present in m-2 and m-1, then absent in m and m+1. A single-crawl flicker — absent, present, absent, present — is rejected as crawl noise.
Figure 4 — The event definitions. Two-crawl persistence promotes a marker change to an adoption (or removal) event; a one-month flicker is rejected. Removal events are the symmetry test: if a plugin truly helps, validated removals should give the gain back.

The delay-JavaScript cohort is detected separately from WP Rocket as a brand, because its markers are unusually strong and feature-specific. WP Rocket tags every delayed script with type="text/rocketlazyloadscript", swaps src for data-rocket-src, and adds companion data-rocket-* attributes. The cohort is defined by raw response-body detection of any of:

type="text/rocketlazyloadscript"   OR   data-rocket-src
data-rocket-type                   OR   data-rocket-status

For the INP analysis, the primary delay-JS cohort is restricted to outcome months of March 2024 or later, because INP officially replaced FID as a Core Web Vital on 12 March 2024. Earlier INP data is used only as a sensitivity check, with release-note checks for schema continuity. Every marker is validated before any estimate is read:

Marker ontology (summary). Brand markers for NitroPack and LiteSpeed are treated as candidate until validated against HTTP Archive positives; the full ontology ships in the replication package.
TreatmentPrimary marker familyConfidence rule
WP Rocket delay-JSrocketlazyloadscript, data-rocket-*Source-backed · two-crawl persistence + manual review
WP Rocket (brand)Wappalyzer + WP Rocket body/header/path markersHigh-confidence sample only; validated apart from delay-JS
NitroPackWappalyzer + candidate nitro-* / cache markersCandidate — manual validation required before inference
LiteSpeed CacheWappalyzer + LSCache / QUIC.cloud server-cache markersStratify server-backed cache vs plugin-only optimisation

The validation protocol is explicit: draw 200 positive and 200 negative origin-months per marker family, stratified by rank and month; inspect bodies and headers to estimate precision; compare Wappalyzer-only, raw-marker-only and intersection positives; treat any family below 0.95 precision as secondary or apply measurement-error correction; and re-run the event study on high-confidence detections only. Precision and recall bounds are published before the estimates are.

Outcomes & thresholds

The outcome hierarchy is fixed before any result is inspected — the discipline that stops a study from quietly migrating to whichever metric happened to move.

The pre-registered outcome hierarchy: each outcome, its tier, and the role it plays in the study
TierOutcomeRole
PrimaryMobile good-LCP share & LCP passLoading — H1's main target
PrimaryMobile good-INP share & INP pass (post-Mar 2024)Interaction — H2/H3
PrimaryMobile good-CLS share & CLS passStability
PrimaryAll-CWV pass (LCP, INP, CLS all Good at p75)The headline users are sold
Secondaryp75 LCP / INP / CLSContinuous effect sizes
SecondaryTTFB, FCPMechanism outcomes
MechanismPage weight, JS bytes, image bytes, requests, third-party count, Lighthouse TBTFrom HTTP Archive — the lab/mechanism contrast, never the primary endpoint

Thresholds follow Google's published Core Web Vitals definitions: Good is LCP ≤ 2,500 ms, INP ≤ 200 ms and CLS ≤ 0.1, assessed at the 75th percentile; the overall assessment passes only when all three are Good where there's sufficient data. p75 units are verified against the live CrUX schema before use.

Matching & control construction

Each adopter is compared with origins that could plausibly have adopted in the same month but didn't. Controls are WordPress origins present in both CrUX and HTTP Archive, with enough pre-treatment history, not using the focal plugin before the event — and a not-yet-treated origin stays eligible as a control only until its own adoption month. The minimum matching set:

  • Timing & platform — event month, form factor, WordPress (and version band where available).
  • Popularity — rank bucket or a log-rank spline.
  • Baseline trajectory — pre-adoption level and six-month pre-trend for LCP, INP, CLS and all-CWV pass; baseline TTFB/FCP and page-weight/JS/image bytes.
  • Stack — host/CDN/server category from headers and Wappalyzer; theme, page builder and e-commerce detections.
  • Third parties — ad stack, tag manager, analytics and consent-management detections.
  • Geography — baseline country/device mix where CrUX country coverage is sufficient.

The method is coarsened exact matching on event month, WordPress, rank band, baseline CWV status and host/CDN category, then nearest-neighbour propensity matching within strata. The propensity model is a balance tool, not the causal model. Balance is judged by standardised mean differences below 0.10, distributional distance, common-support and pre-treatment outcome balance — and where balance fails, the design declares the effect unidentified in that stratum rather than reporting it anyway.

Estimation strategy

Why not a plain two-way fixed-effects event study? Because adoption is staggered and effects are heterogeneous, and under those exact conditions a naïve TWFE regression uses already-treated units as controls for later-treated ones — the Goodman-Bacon decomposition shows this can flip the sign of a true effect. The primary estimator is therefore a modern group-time estimator — Callaway & Sant'Anna's group-time ATT (with not-yet-treated controls), cross-checked against Sun & Abraham's interaction-weighted event study. Plain TWFE is reported only as a diagnostic, never as the headline.

Effects are aggregated with cohort-size weights and reported dynamically for k = −6…+12, with three focal horizons: a short run (k = +1…+3, after the timing ambiguity clears), a medium run (+4…+6, after caches and image-optimisation queues settle), and persistence (+7…+12). Standard errors cluster at the origin with a block bootstrap; Benjamini-Hochberg false-discovery control spans the plugin × metric × form-factor family, while unadjusted intervals are preserved for effect-size reading. The transparent DiD representation — useful to show, not as the sole estimator — is:

Y_it = α_i + γ_t + Σ_(k ≠ −1) β_k · 1[event_time_it = k] + X_it'θ + ε_it

α_i   origin fixed effects        γ_t   calendar-month fixed effects
k=−1  omitted reference month     β_k   the event-time coefficients (leads + lags)

The coefficient plot must show leads as well as lags. A positive post-adoption estimate is not credible if the leads are already drifting up before the install — which is exactly what the next section is built to catch.

Identification threats & built-in defences

Each identification threat to the study, why it matters, and the design defence against it
ThreatWhy it mattersDesign defence
Selection into adoptionPerformance-aware owners & better hosts adopt moreMatch on rank, host/CDN, theme, baseline level & pre-trend
Regression to the meanSlow sites improve regardless of any pluginMatch on pre-trend & baseline severity; placebo adoption dates
Simultaneous changesHost/theme/CDN/core often change at the same timeExclude or stratify origins with detected stack changes near the event
CrUX timingCrawl month ≠ CrUX 28-day windowOmit/down-weight month m; post-period starts at m+1
Detection errorMarkers can be hidden, removed or falsely firedPersistence rule, Wappalyzer × raw comparison, manual validation
AttritionOrigins enter/exit CrUX as traffic changesModel inclusion; report balanced & unbalanced panels
Multiple treatmentsSites stack several optimisation pluginsSingle-treatment primary sample; multi-treatment as a stratum
Geography shiftAudience mix moves p75 on its ownUse country_summary; control/stratify by geo mix
Field–lab mismatchLab gains needn't reach usersCrUX is primary; Lighthouse is falsification only
Staggered-DiD biasTWFE compares to already-treated unitsGroup-time ATT / Sun-Abraham as primary

Falsification & robustness

A field-positive verdict has to survive a battery of attempts to break it. These are pre-committed, not chosen after seeing results:

What would count as “works in the field”?

The verdict is per plugin, per metric, per stratum — and deliberately resistant to vendor-friendly rounding:

  • Field-positive — post-adoption CrUX ATT is practically and statistically positive in the expected direction; pre-trends pass; removals are consistent where observable; the result survives the high-confidence marker sample.
  • Field-neutral — estimates small, unstable or centred on zero after matching, even if lab metrics improved.
  • Field-negative — field outcomes deteriorate after adoption; the watch case is INP for delay-JS cohorts.
  • Lab-only — Lighthouse/TBT improve materially while CrUX does not move. The score went up; the users didn't notice.
  • Unidentified — matching fails, markers fail validation, pre-trends fail, or simultaneous stack changes dominate. Reported as unidentified, not as zero.

A single global yes/no is unlikely to be honest. The defensible answer will be conditional — something like: plugin X lifts mobile LCP for slow shared-host origins; plugin Y has small average effects but larger ones when baseline TTFB is poor; delay-JS improves lab and sometimes LCP but is ambiguous-to-negative on INP for interaction-heavy templates. That conditional map is the product practitioners actually need.

Limitations — read before quoting

  1. Page vs origin

    HTTP Archive crawls a root page (and, since 2022, a secondary page for many origins) while CrUX is origin-level. A plugin may help product pages more than the home page, or vice-versa; the origin outcome blends them.

  2. Coverage floors

    Low-traffic origins are absent from CrUX, and CrUX reflects eligible Chrome users, not all browsers. Effects generalise to sites big enough to be measured.

  3. Detection blind spots

    Wappalyzer and raw markers can miss hidden or aggressively-optimised implementations; brand markers for NitroPack and LiteSpeed start as candidates until validated.

  4. Configuration is partly unobservable

    Plugin effects depend on settings (which caches, which exclusions) that response bodies only partly reveal — so estimates are intent-to-treat by detected state, not by optimal configuration.

  5. It's still observational

    Matching and pre-trend tests harden the design, but only a randomised install would close every door. The correct response is to validate detection, stratify hard, and refuse to generalise past identified strata — not to retreat to lab scores.

Data, code & sources

The full replication package is published under CC BY 4.0: the source registry, the marker ontology, the target panel schema, the BigQuery extraction templates, and the matching / event-study / reporting scripts — everything needed to produce the estimates this article deliberately withholds. Run it, disagree with it, or extend it to plugins we didn't cover.

Browse the artefacts

Key sources

Cite this study

PageSpeedAudit Research (2026). Do WordPress speed plugins work in the field?
A causal adoption-cohort design for WP Rocket, NitroPack, LiteSpeed Cache and
delay-JavaScript using HTTP Archive and CrUX.
https://pagespeedaudit.com/research/do-wordpress-speed-plugins-work

@misc{pagespeedaudit2026wpplugins,
  title  = {Do WordPress speed plugins work in the field? A causal
            adoption-cohort design using HTTP Archive and CrUX},
  author = {{PageSpeedAudit Research}},
  year   = {2026},
  url    = {https://pagespeedaudit.com/research/do-wordpress-speed-plugins-work},
  note   = {Registered protocol + replication package, CC BY 4.0}
}

Free 4-part email series

The WordPress speed series

The evidence standard behind this protocol, applied to your own site — four short emails, then it stops. Email-only; not published on the site:

  1. Part 1The three layers every speed claim must survive — mechanism, lab, field.
  2. Part 2View source — what your plugin actually changed, and how to judge it in field data.
  3. Part 3Why "sites with plugin X have better vitals" proves nothing — and what would.
  4. Part 4Your WordPress PageSpeed report — which flags are Real, and the silence to check first.

Double opt-in: we email a confirmation link first. Four emails, then it stops — unsubscribe any time.

Don't want to wait for the census — is your plugin working?

A PageSpeedAudit report doesn't forward a vendor's claim or a Lighthouse number. It reads your real critical path and your CrUX field data, and tells you which fixes — and which plugins — are actually moving LCP and INP for your users, and which are only moving the score. Our sister study “Do Lighthouse's promised savings materialize?” measures the same lab-vs-reality gap fix-by-fix, and the free cloaking test checks whether a “95+” is even real.