All articles
62 articles · updated weekly See our Tools
All articles
Tutorials

How to Measure Website Speed Correctly

Lab data finds the problem, field data confirms you fixed it. A practical guide to PageSpeed, Lighthouse, WebPageTest, and CrUX — and which number Google actually uses for ranking.

COVER · Tutorials

The client opened PageSpeed Insights, saw 72/100, and figured things were fine. A week later, Google Search Console showed 38% of URLs failing LCP in the Core Web Vitals report. No contradiction — those were different data sources measuring different things. The 72 score was lab data; the failures were from the real world.

That's the central problem with measuring web performance: the tools exist, the data exists, but what each number actually means depends entirely on where it came from.


Lab data vs field data: the distinction that changes everything

Every performance number you encounter belongs to one of two categories.

Lab data is synthetic. A tool simulates a device, a network connection, and loads the page once under controlled conditions. Lighthouse uses an emulated Moto G Power on a slow 4G connection (10 Mbps, 40 ms RTT) as its baseline. WebPageTest lets you choose the actual machine, location, and throttling method. The result is reproducible and isolated — useful for debugging, useless for representing real users.

Field data is collected from real users. Chrome instruments performance metrics silently for users who've opted into sharing data, and those measurements are aggregated in CrUX (Chrome User Experience Report). CrUX feeds PageSpeed Insights and Search Console. It captures users on budget Android phones in rural areas, on iPhone 14s with fiber, on 3G connections in low-signal zones — the full diversity of your actual audience.

The practical implication: lab data is where you find the problem. Field data is where you confirm you fixed it.


What each tool actually measures

PageSpeed Insights

PageSpeed Insights (PSI) runs Lighthouse under the hood, but it isn't just Lighthouse. The crucial difference is that PSI layers CrUX data on top of the lab result — when your site has enough traffic to have a CrUX dataset.

The interface has two distinct blocks:

  • "Field Data" (top) → 28 days of real CrUX data at the 75th percentile
  • "Lab Diagnostics" (below) → Lighthouse running on Google's servers

These are different instruments. The 0–100 score is from the lab. The LCP/INP/CLS bars that determine whether a URL passes Core Web Vitals are from the field. That lab score doesn't affect rankings — what Google uses for ranking is the field data.

A URL with a lab score of 95 can fail in the field if your real users are on devices slower than the simulated Moto G Power. The inverse exists too: score 60 in the lab, but green in the field, because your users are all on iPhones with fast connections.

Lighthouse

Lighthouse is the foundation of PSI, but you use it in distinct ways depending on context:

  • DevTools: runs on your machine, with your cache, your extensions, your hardware. Good for quick comparisons during development — but absolute numbers are meaningless without context.
  • CLI: npx lighthouse https://example.com --throttling-method=simulate in a controlled environment. Good for CI/CD — catching regressions between deploys before they reach production.
  • PSI: runs on Google's servers, no local cache, no extensions, consistent hardware. The most reliable Lighthouse result for comparing against competitors.

One implementation detail that changes results: Lighthouse uses software throttling — it simulates latency by adding programmatic delays. WebPageTest uses packet-level throttling — it delays traffic at the OS level. Packet-level throttling is more physically accurate and typically produces worse (more realistic) metrics than Lighthouse for the same simulated connection.

WebPageTest

WebPageTest is the most powerful free frontend performance analysis tool available. What it offers beyond Lighthouse:

  • Filmstrip: frame-by-frame screenshots of the loading process — you see exactly when the page became visually complete
  • Detailed waterfall: every resource, with timing for DNS lookup, TCP connect, TTFB, download, blocking time
  • Multiple locations: test from Brazil, US, Europe, Asia in a single session
  • Multiple runs: 3–5 runs with median and standard deviation — Lighthouse only runs once
  • Real user simulation via WebDriver: simulate clicks and interactions to measure INP in specific flows

WebPageTest is the right tool when Lighthouse says everything is fine but users complain about slowness, or when you need to compare performance across geographic locations.

CrUX and Search Console

CrUX aggregates Chrome data at three granularities: specific URL, similar page groups (origin-level), and full domain. Google Search Console exposes this data with filters by device type and URL.

One thing that trips people up: if a URL doesn't have enough traffic, CrUX won't have sufficient data for it individually. In that case, Search Console shows domain-level data as a fallback. A new post has no field data — Lighthouse is all you have until you accumulate enough visits.

The "Core Web Vitals" report in Search Console is the most actionable view for SEO: it lists URLs grouped by problem, with status "poor/needs improvement/good." That's where you prioritize what to fix.


Why Lighthouse lies (and when to trust it)

The Lighthouse score is a weighted average of metrics with different weights. In 2026, the approximate weights are: LCP 25%, TBT 30%, Speed Index 10%, FCP 10%, CLS 25%. TBT (Total Blocking Time) is the lab proxy for INP — it measures blocked main thread time, which correlates with slow interactions.

The problem is that high TBT in the lab doesn't always translate to high INP in the field — and vice versa. A site with many complex event listeners can have low TBT (no long tasks during loading) but high INP (every click triggers heavy work). Lighthouse doesn't capture that.

Another caveat: Lighthouse measures the initial load. If your page is an SPA that fetches data after mount, Lighthouse may miss the real experience — data arrives after the measurement window. Field data catches this.

The cases where Lighthouse misleads most:

  1. Auth-gated sites — Lighthouse doesn't log in, so it tests the public landing page, not the actual product
  2. Pages with personalized content — A/B tests, banners based on geolocation
  3. Heavy client-side rendering — the score can look fine but users see a spinner for 3 seconds

For a deep dive into LCP, INP, and CLS — what each threshold means and how to fix each one — the post Core Web Vitals: LCP, INP, and CLS Explained covers that in detail.


The number Google uses for ranking: 75th percentile

Core Web Vitals are evaluated at the 75th percentile of real sessions collected by CrUX over the past 28 days. Not the average. The 75th percentile.

This means: 75% of your users need to have LCP ≤ 2.0s (the current 2026 threshold, after Google's March 2026 adjustment), INP ≤ 200ms, and CLS ≤ 0.1 for the page to pass. The slowest 25% determine whether you fail.

The practical implication: optimizing only for the average case is not enough. If your site is fast on fiber but you have users on 4G, those slowest 25% are what determines whether you pass.

As of May 2026, 55.9% of origins tracked by CrUX pass all three Core Web Vitals simultaneously. Nearly half the web still fails. Individual metric pass rates: 68.6% pass LCP, 81.3% pass CLS, 86.6% pass INP.


A measurement workflow that actually works

Measuring once isn't monitoring. Performance regresses — a new deploy, an unoptimized image, a third-party script. The setup that works in practice:

During development:

# Lighthouse CLI integrated in CI
npx lighthouse https://staging.example.com \
  --output json \
  --output-path report.json \
  --chrome-flags="--headless"

# Check minimum threshold (e.g., fail build if LCP > 2.5s)
node -e "
const r = require('./report.json');
const lcp = r.audits['largest-contentful-paint'].numericValue;
if (lcp > 2500) process.exit(1);
"

After production deploy:

  • PageSpeed Insights to check if field data is still green
  • Search Console to catch new URLs accumulating failures

For debugging specific problems:

  • WebPageTest with filmstrip to see exactly when the LCP element appears
  • DevTools Performance panel to identify long tasks tied to interactions (INP)
  • web-vitals library instrumented in your code to collect custom field data:
import { onLCP, onINP, onCLS } from 'web-vitals';

// send to your analytics
onLCP(({ value, id }) => analytics.track('web_vital', { metric: 'LCP', value, id }));
onINP(({ value, id }) => analytics.track('web_vital', { metric: 'INP', value, id }));
onCLS(({ value, id }) => analytics.track('web_vital', { metric: 'CLS', value, id }));

With this setup, you have your own field data — not dependent on CrUX having enough volume — and you can filter by country, device, app version.


Frequently asked questions

Why does my PageSpeed score change every time I run it?

Lab data has natural variance — concurrent processes on Google's servers, different CDN states, third-party request timing. Differences of ±5 points between runs are normal. What matters is the trend across multiple runs, not the number from a single run. WebPageTest runs multiple times and shows the median for exactly this reason.

Which tool should I use to measure site speed: PageSpeed or Lighthouse?

It depends on what you're asking. If the question is "are my real users having a good experience?", use PageSpeed Insights and look at the field data block. If the question is "what's causing the slowness so I can fix it?", use Lighthouse in DevTools or WebPageTest for the detailed waterfall. They're complementary instruments, not substitutes.

Does a high Lighthouse score guarantee good rankings?

Not directly. The lab score is not what Google uses for ranking — it's the Core Web Vitals from the field (CrUX). You can have a 90 score in the lab and fail in the field. What counts for SEO is the 75th percentile of your real users' metrics, not the synthetic simulation result.

My site is new and has no Search Console data. What do I do?

For new sites, CrUX takes a few weeks to accumulate sufficient data. In the meantime, work with lab data: run Lighthouse on the production version (not localhost), use throttling conditions that represent your audience, and instrument the web-vitals library to collect your own field data from the start.


Measure correctly before you optimize

Performance in the browser is measured in layers — and each layer can mislead about the others. Lab scores are useful for finding problems and catching regressions in CI. Field data is what determines whether real users are having a good experience and whether Google will rank you accordingly.

The most common mistake I see is treating the Lighthouse score as the end goal. The goal is getting the 75th percentile of field data into the green — LCP ≤ 2.0s, INP ≤ 200ms, CLS ≤ 0.1. Lighthouse is a diagnostic instrument on the way there, not the finish line.

When auditing the SEO meta tags of a site during a performance pass — title, description, og:image — I use the Quick Tools Meta Tag Generator to preview how it renders before shipping.

RD
Author
Rafael Duarte
Desenvolvedor backend com passagem por fintech e SaaS B2B — trabalhou em times que escalaram APIs de zero a milhões de requisições. Carrega cicatrizes de produção suficientes para ter opiniões fortes sobre ferramentas, padrões e decisões de arquitetura. Não é acadêmico: leu a RFC do UUID quando precisou escolher entre v4 e v7 para uma tabela de alta escrita.
View profile