Performance
Website Speed Audits: A Repeatable Process That Works
A fixed process for running speed audits that actually produce comparable results over time — the test-set discipline, the lab-vs-field data distinction, and how to prioritize fixes that move the needle.

A speed audit that runs Lighthouse once, screenshots the score, and calls it done isn't a process — it's a snapshot, and snapshots don't tell you whether you're improving, regressing, or just seeing noise from a different test run. The teams that actually get faster over time treat speed audits as a repeatable measurement, run the same way every time, against the same URLs, so this month's number means something relative to last month's. Here's the process we run on client sites, monthly at minimum and after every significant release.
Step one: fix the test set, and don't change it casually
Pick a small, representative set of URLs and commit to testing exactly those, every time: the homepage, one high-traffic landing page, one list/archive page (blog index, product category), and one content-detail page (a blog post, a product page). Four to six URLs is usually enough to represent a site's different template types without the audit itself becoming a chore that gets skipped.
Consistency of environment matters as much as consistency of URLs. Fix a device/network profile — Lighthouse's "Mobile" preset with its throttling is the reasonable default, since mobile is both the harder case and, for most sites, the majority of traffic. Test from a consistent location if you're running audits locally rather than in CI, since network path affects TTFB meaningfully.
Run each URL multiple times and use the median, not the first result. Lighthouse scores vary run to run — background processes, network jitter, even browser cache state between runs introduce noise. Three runs, median score, is enough to smooth out most of that noise without making the audit take all afternoon.
Step two: know which number you're actually looking at
Lab data (Lighthouse, PageSpeed Insights' lab section) is reproducible and immediately available — you can test a page that just went live and get a number instantly. It's synthetic: a simulated device and network, not real users.
Field data (Chrome UX Report / CrUX, surfaced in PageSpeed Insights and Search Console) reflects real Chrome users who actually visited the page, aggregated over the trailing 28 days. It's the data Google actually uses for the Core Web Vitals ranking signal, and it's frequently worse than lab data — real users are on real networks, real devices, with real extensions and real multitasking, none of which a lab test simulates.
Report both, explicitly, rather than picking whichever looks better: "Lab says LCP is 2.1 seconds on the homepage; CrUX's 75th percentile says 2.8 seconds." The gap between the two numbers is itself informative — a large gap often means real-world conditions (slower connections, lower-end devices in your actual audience) are worse than your test environment assumes, which changes what's worth prioritizing.
Set a plain bar and track pass/fail against it, not just the raw number: LCP under 2.5 seconds, INP under 200 milliseconds, CLS under 0.1 — Google's published "good" thresholds. "Did the test set pass this month" is a much more useful team-facing metric than a Lighthouse score that fluctuates for reasons unrelated to your actual changes.
Step three: prioritize by impact, not by what Lighthouse lists first
Lighthouse's Opportunities and Diagnostics sections are a starting point, not a priority order — they don't know which fix will move your specific score the most. In practice, three categories account for most real-world LCP and INP problems, roughly in order of how often they're the actual bottleneck:
- The LCP element itself — usually a hero image or a large block of text behind a webfont. Check whether it's optimally sized, served in a modern format, and not blocked by render-blocking CSS/JS ahead of it.
- Render-blocking resources — CSS or synchronous JS that delays first paint. Critical CSS inlined, everything else deferred or async, is the standard fix.
- Main-thread work — third-party scripts (analytics, chat widgets, ad tech) are disproportionately responsible for poor INP, because they execute JavaScript you don't control on a timeline you don't control.
Fix the single highest-impact item, re-run the audit, confirm the gain, then move to the next one. Don't batch five changes together and re-test once — if the score doesn't move as expected, you won't know which change (or which change canceling out another) was responsible. This is slower per-fix but faster overall, because it doesn't produce mystery regressions three fixes later.
Don't chase a perfect Lighthouse score. A stable "good" across all three Core Web Vitals, sustained over time, delivers essentially all the ranking and user-experience benefit; the marginal effort to go from 92 to 100 is rarely worth diverting from other priorities.
Step four: make it a process, not a one-off
Schedule audits — after every significant release at minimum, monthly as a baseline even without a release, since third-party scripts and content changes can regress performance without any code change on your part (a marketing team adding a new tracking pixel is a common, invisible culprit).
Automate where you can. Lighthouse CI, run against preview deployments or on a schedule, can fail a build or flag a Slack channel when a metric regresses past a threshold — this catches performance regressions at the same stage you catch broken builds, rather than a month later when a client asks why the site feels slower.
Store results with history, even something as simple as a spreadsheet or a JSON log committed alongside the audit script — the value of a repeatable process compounds specifically because you can see the trend line, not just the current number.
Assign ownership. Someone specific checks the monthly report and follows up on regressions — "the team" checking a report is equivalent to nobody checking it. This is the single change that turns a good intention into a process that actually runs for more than two months.
The process, condensed
| Step | What to fix | Why |
|---|---|---|
| Test set | Same 4–6 URLs, every time | Comparable results over time |
| Environment | Same device/network profile, median of 3 runs | Removes run-to-run noise |
| Data | Report lab AND field (CrUX) explicitly | Field data is what actually ranks |
| Prioritization | One fix at a time, re-test, confirm | Avoids mystery regressions |
| Cadence | Every release + monthly baseline | Catches drift from third-party scripts |
| Ownership | One named person reviews monthly | Reports nobody owns get ignored |
What this actually buys you
A repeatable audit process doesn't make a site fast by itself — the fixes still have to happen. What it buys you is the ability to tell, with actual evidence, whether the site is getting faster or slower over time, and to catch regressions while they're a five-minute fix instead of a mystery three months later when someone finally notices bounce rate creeping up.
We run this process as part of every performance and SEO engagement we take on, and it's built into the release flow for ongoing product engineering retainers so drift gets caught automatically. If your team runs speed checks occasionally but doesn't have a consistent process behind the numbers, get in touch — setting this up properly is usually a day of work, and it pays for itself the first time it catches a regression before a client does.