Short answer. Website speed testing splits into two classes. Lab runs (Lighthouse, PageSpeed Insights, GTmetrix, WebPageTest) load the page on a synthetic device over a simulated network. Field data (CrUX, Search Console, your own RUM) is collected from live visitors. The classic "PSI says 95 but users complain" gap is exactly the difference between lab and field — not a bug in the tool.
This article is not a "top 10 services" link list. It explains what each class of tool actually measures, why two consecutive runs return different numbers, which metrics matter in 2026, and how to build a measurement you can trust. Optimization itself lives in separate guides — here we only cover measurement and how to read it.
What website speed test tools actually measure
"Site speed" is not a single number. Every modern tool breaks loading down into three independent groups of characteristics, and they fail for different reasons.
- Byte delivery. How long the server thinks before answering and how much data reaches the browser: TTFB, page weight, request count, compression. Owned by the backend, the hosting and the CDN.
- Rendering. When the user sees anything at all (FCP), when they see the main thing (LCP), and how smoothly the page fills in (Speed Index). Owned by the critical rendering path: CSS, fonts, images.
- Interactivity and stability. Whether the page responds to clicks (TBT in the lab, INP in the field) and whether the layout jumps under the user's finger (CLS). Owned by JavaScript and markup.
A site can serve HTML in 80 ms and still feel unbearably slow because three megabytes of JavaScript block the main thread for two seconds. The reverse is also true: a heavy TTFB from a server on another continent ruins even a perfectly built page. That is why a single aggregate score is nearly useless without the per-metric breakdown — and that breakdown is where reading any report should start.
Rule. Never compare "scores" between different tools. Lighthouse, GTmetrix and any homegrown checker compute their own totals with their own weights. Only identical metrics captured the same way are comparable.
Lab data vs field data: the distinction that breaks people's mental model
This is where most confusion lives. Let's take it apart.
What a lab run is
Lab data (synthetic) means a single page load under controlled conditions: a specific browser, a fixed network profile, a fixed CPU budget, a cold cache, no extensions and no live human. The tool opens the page, waits for activity to settle and reports metrics. That is how Lighthouse, the lab half of PageSpeed Insights, GTmetrix, WebPageTest and any synthetic monitor work.
Strength: reproducibility and diagnostics. You get a trace, a request waterfall, a list of specific resources, and you can verify the effect of a change in a minute. Weakness: these are not your users. This is a hypothetical user on a device you picked.
What field data is
Field data (RUM — real user monitoring) is metrics captured in the browsers of actual visitors and aggregated. The main public source for Chrome is the CrUX dataset, which feeds both PageSpeed Insights and the Core Web Vitals report in Search Console. Key properties:
- Aggregation runs over a rolling window of roughly 28 days — the data always lags and will not reflect yesterday's release.
- Metrics are evaluated at the 75th percentile: "good" means three quarters of loads met the threshold, not that the average did.
- Only Chrome users who opted into usage statistics are included. Safari, Firefox and locked-down corporate builds are invisible in CrUX.
- If a specific URL has little traffic, there will be no page-level data — the tool falls back to origin-level aggregates or reports insufficient data.
Why "PSI says 95 but users complain"
Typical causes, from most to least common:
- INP is not measured in the lab at all. It is an interaction metric: it needs a human clicking things. A lab run uses a proxy — TBT. A page with a green lab score can still have terrible field INP.
- Your audience is heavier than the synthetic profile. The lab's "average phone" may well be faster than the devices your visitors actually own, and a data-centre link is faster than mobile internet in the suburbs.
- The lab never sees your post-consent code. Consent banners, support chats, ad pixels and A/B scripts often load only after user action or only for part of the traffic.
- Different pages. You benchmark the home page while complaints are about a product page with twenty images and a faceted filter.
- Logged-in users. An account area is usually heavier than the public site and never appears in a lab run.
The reverse case: "PSI says 40 but the field is green"
Also a normal situation. The mobile lab run is deliberately brutal: a weak CPU and a slow link. If your real audience is office desktops on wired connections, the field will be green while the lab score is low. Conclusion: a low score is a reason to look at what is slow, not a reason to panic. Prioritization decisions come from the field.
Working rule. The field answers "is there a problem and for whom". The lab answers "why, and what do I fix". You need both: field only means you know about the pain but not the cause; lab only means you fix things nobody is bothered by.

Speed metrics: LCP, INP, CLS, TTFB, FCP, TBT
The current Core Web Vitals set consists of three metrics: LCP, INP and CLS. FID (First Input Delay) was retired from Core Web Vitals in spring 2024 and replaced by INP. If a tool or checklist still tells you to "improve FID", it is out of date — target INP instead. A deep dive into the new metric is in our guide to INP in Core Web Vitals, and the full picture is in the Web Vitals guide.
| Metric | What it measures | Typical cause of regression | Where to look |
|---|---|---|---|
| LCP (Largest Contentful Paint) | When the largest visible element paints: a hero image, a banner or a block of text | Heavy image in a legacy format, lazy loading applied to the LCP element, a font blocking text, slow TTFB | The waterfall: which resource is the LCP element and when it started loading. Check preload and fetch priority |
| INP (Interaction to Next Paint) | The delay between a user action and the next paint; the worst interaction of the visit is reported | Long JavaScript tasks, heavy event handlers, synchronous rendering of large lists, third-party widgets | Field data only, or manual testing in DevTools. In the lab, indirectly via TBT and long tasks |
| CLS (Cumulative Layout Shift) | Total unexpected layout movement over the page's lifetime | Images and iframes without dimensions, late banners and cookie bars, font swap, injected ads | DevTools Performance, the Layout Shift Regions overlay; in reports, the list of shifted elements |
| TTFB (Time to First Byte) | Time until the first response byte: DNS, TCP, TLS, server processing, network | Slow backend or database, missing cache, CDN miss, redirect chains, geographic distance | Terminal: curl -w with the full timing breakdown. Then application logs and profiling |
| FCP (First Contentful Paint) | The moment the user sees the first piece of content | Render-blocking CSS and JS in head, a heavy web font, high TTFB | The waterfall: what sits between the HTML and the first paint |
| TBT (Total Blocking Time) | Total time the main thread was blocked by tasks longer than 50 ms | Large bundles, SPA hydration, analytics and tag-manager payloads, polyfills | The Performance tab, Long Tasks; in Lighthouse, "Reduce JavaScript execution time" |
| Speed Index | How quickly the visible viewport fills in | Late images, entrance animations, deferred content rendering | The load video and filmstrip in WebPageTest or GTmetrix |
What counts as a good value
The thresholds below are the widely accepted reference points used by public tools. They are evaluated at the 75th percentile of field data, not on a single run. Splitting mobile and desktop is mandatory: the same page almost always lands in different buckets.
| Metric | Good | Needs improvement | Poor | Available in |
|---|---|---|---|---|
| LCP | up to 2.5 s | 2.5–4.0 s | over 4.0 s | lab and field |
| INP | up to 200 ms | 200–500 ms | over 500 ms | field only |
| CLS | up to 0.1 | 0.1–0.25 | over 0.25 | lab and field |
| TTFB | up to 0.8 s | 0.8–1.8 s | over 1.8 s | lab, field, terminal |
| FCP | up to 1.8 s | 1.8–3.0 s | over 3.0 s | lab and field |
| TBT | up to 200 ms | 200–600 ms | over 600 ms | lab only |
Important. TTFB is not a Core Web Vital and does not affect the page assessment directly. But it is a component of LCP: while the server thinks for a second, you have a second and a half left for everything else. That is exactly why TTFB is measured separately and fixed first.
Why the Lighthouse score is not "speed"
The number in the coloured circle is neither a load time nor a "quality percentage". It is a weighted composition of several lab metrics mapped onto a non-linear curve. In current Lighthouse versions the contributions are roughly: TBT about 30%, LCP about 25%, CLS about 25%, FCP and Speed Index about 10% each. The exact weights change between versions, which is one reason a score can move "on its own" after a tool update.
Several non-obvious consequences follow:
- INP is not part of the score. At all. A Lighthouse score can be green on a page that takes half a second to answer a click.
- One metric sinks everything. TBT carries a third of the weight: a single heavy third-party script can wreck the score despite excellent LCP and CLS.
- The curve is non-linear. Moving from 30 to 55 is usually cheap; moving from 90 to 95 is expensive and nearly invisible to users. Chasing 100/100 is bad economics.
- The score is not a metric for a management report. It does not convert into seconds. For reporting, use field LCP and INP — those translate into "the user waits this long".

Why two consecutive runs give different scores, and how to measure properly
Where the variance comes from
A 5–15 point spread between two runs of the same unchanged page is normal, not a malfunction. Sources:
- Machine resources. A local Lighthouse run shares the CPU with your tabs, your bundler and your antivirus. Public services share infrastructure with other users.
- Throttling is simulated. The default mode does not physically shape traffic; it recalculates timings from a model, and the model is sensitive to how the run actually went.
- Your site is not deterministic. A/B tests, rotating banners, different ad creatives, random products in a recommendation block — all of it changes page weight from run to run.
- Caches. The first run hits a cold CDN cache and a cold application cache; the second hits warm ones. The TTFB difference can be several-fold.
- Third-party hosts. Analytics and widgets live their own life; their latency is entirely outside your control.
How to measure properly
- Run at least five times, preferably nine or eleven (an odd count makes the median trivial).
- Take the median, not the mean: one outlier moves the mean and leaves the median alone.
- Freeze the profile: same device, same throttling mode, same region.
- Test exactly one URL with identical query parameters. A UTM tag can bypass a cache and change behaviour.
- Compare before and after within the same time window. A morning baseline and an evening re-test are not comparable.
- Record the tool version. A Lighthouse upgrade changes weights and silently rewrites your mental baseline.
Mobile runs vs desktop runs
Mobile mode is not just a narrow viewport. The tool additionally slows the CPU (typically by a factor of four relative to the host machine) and constrains the network to a slow mobile profile with noticeable latency. The point is to model a budget Android device, not a flagship.
Practical consequences:
- The mobile score is almost always lower than desktop, and that is expected. Comparing mobile to desktop is meaningless.
- CPU throttling punishes JavaScript-heavy sites hardest: TBT multiplies, and a third of the score goes with it.
- Core Web Vitals are assessed separately for mobile and desktop users. If your traffic is mostly mobile, a green desktop run is false comfort.
- The absolute effect of throttling depends on the host machine. A run on a developer laptop and a run on a CI runner produce different numbers with identical settings.
How not to fool yourself
The most common ways to get a pretty but false number:
- Running with a warm browser cache. You have opened this page twenty times already — the static assets are local. A real visitor arrives empty-handed. Measure in a private window or with a tool that guarantees a clean profile.
- Browser extensions. An ad blocker strips half of the third-party scripts and shows you somebody else's site. Password managers and translators add their own delays. Run local Lighthouse in a clean profile.
- Corporate networks. A gigabit office link, a caching proxy and a data centre close to your servers produce numbers none of your customers will ever see.
- Logged-in admin. Admin toolbars, caching disabled for authenticated users, framework debug bars — you are measuring a different page.
- Dev builds. Unminified bundles, source maps, hot reload. Measure the production build on the production domain only.
- A fresh deploy. For the first few minutes caches are cold and background jobs are warming the application. Let the system settle.
Website speed test tools compared by class
Tools differ not in "quality" but in the class of job they do. Below is what each one really gives you and, more importantly, what it fundamentally will not show.
| Tool | Lab or field | Strength | What it will NOT show | Best for |
|---|---|---|---|---|
| Lighthouse in Chrome DevTools | Lab | Full audit with a trace on your own machine; works on localhost and behind authentication | Field data, real-user INP, behaviour on other devices | Developers, local debugging before deploy |
| PageSpeed Insights | Lab + field (CrUX) | The one free place where a lab run and field Core Web Vitals sit side by side | Request waterfall, run history, authenticated pages and local builds | Site owners and SEO, fast first diagnosis |
| WebPageTest | Lab (multi-region, real devices and network profiles) | The most detailed waterfall, connection view, filmstrip, side-by-side comparison of two runs | Field metrics for your audience; INP as a real interaction metric | Performance engineers, deep diagnostics |
| GTmetrix | Lab | Readable report with a waterfall and a load video, plus scheduled run history | CrUX field data for your users; behaviour of authenticated areas | Webmasters, recurring checks of one or two key pages |
| Chrome DevTools, Performance and Network panels | Lab | Exactly what occupies the main thread: long tasks, per-frame layout shifts, call stacks | Any aggregate score; data about other users | Developers, root-causing after a problem is confirmed |
| Search Console, Core Web Vitals report | Field (CrUX) | Groups of URLs that are genuinely bad for users, split by mobile and desktop | The cause of the problem or any diagnostics; recent data — the window is about 28 days | SEO, prioritizing work across site sections |
| Analytics platform speed reports | Field (your visitors) | Data on your actual audience, including browsers and regions missing from CrUX | Core Web Vitals exactly as Google computes them | Sites whose audience is under-represented in Chrome telemetry |
| Your own RUM built on the web-vitals library | Field | Metrics segmented by page template, device, region and release version; no reporting lag | Root cause at the individual resource level — pair it with a lab run | Product teams past the basic optimization stage |
| Scheduled synthetic monitoring | Lab, on a schedule | Trend and regression alerting: you can see which release degraded the metric | The experience of an individual user and rare heavy scenarios | Teams that need control after every release |
| curl and CLI utilities | Lab, targeted | TTFB, headers, compression, redirects — no browser needed, easy to run in CI | Rendering, JavaScript execution, layout shifts, any visual metric | Sysadmins and DevOps checking the server side |
What none of them do. No tool will tell you whether speed is costing you money. The link between a metric and conversion can only be built on your own data: RUM plus analytics, segmenting sessions by LCP and comparing conversion between segments. Public "one second faster equals X percent more revenue" figures are somebody else's case study and do not transfer to your site.
How to read a request waterfall
A waterfall is a timeline where each row is one network request and the length of the bar is its lifetime. The bar is split into phases: queueing, DNS, TCP connection, TLS handshake, waiting for the server and the actual download. Read it top to bottom and left to right.
What to look for, ordered by how often it turns up:
- A long wait phase on the very first request. That is the main document's TTFB. Until it ends the browser knows nothing about the page. Diagnosis lives in our guide to server load troubleshooting.
- Redirects before the main document. Every redirect is another full DNS, TCP and TLS cycle. A chain of "http → https → www → trailing slash" costs hundreds of milliseconds on a mobile link.
- Render-blocking resources in head. Synchronous
<script>tags and external CSS delay the first paint. In the waterfall this looks like an empty stretch before FCP occupied by a couple of files. - Dependency chains. HTML loads CSS, that CSS pulls another stylesheet through
@import, which then pulls a font. Every link is another network round trip. Look for staircases in the waterfall. - Fonts. A web font discovered only after CSS parsing delays text. The tell-tale sign is a font bar starting well after the page begins.
- Missing compression. A text file (CSS, JS, JSON, SVG, HTML) with a suspiciously large size and no content-encoding header. Details in the gzip and brotli guide.
- Images queued ahead of the LCP element. If the browser first fetches ten below-the-fold cards and the hero image is twelfth in line, LCP will be poor no matter how small the file is. Recipes in our image optimization guide.
- Third-party hosts. Every new host means its own DNS, TCP and TLS. Five analytics vendors mean five such cycles.

Measuring TTFB, compression and cache headers from the terminal
Browser tools blend server time with rendering. When you need to know whether the server or the frontend is slow, measure TTFB separately. The most direct way is curl with a full phase-by-phase timing breakdown.
# format file: create it once next to your script
cat > curl-format.txt <<'EOF'
dns_lookup %{time_namelookup}s
tcp_connect %{time_connect}s
tls_done %{time_appconnect}s
redirects %{time_redirect}s
ttfb %{time_starttransfer}s
total %{time_total}s
http_code %{http_code}
size %{size_download} bytes
EOF
# a single measurement with the full timing breakdown
curl -s -o /dev/null -w "@curl-format.txt" https://example.com/
How to read the output:
time_namelookup— name resolution. Consistently above 50 ms points at DNS hosting or a missing resolver cache.time_connectminustime_namelookup— TCP setup. This is pure network latency to the server, roughly one round trip.time_appconnectminustime_connect— the TLS handshake. Usually another one or two round trips; cheaper with session resumption.time_starttransferminustime_appconnect— the application's actual response generation time. This is the number worth taking to a backend engineer.time_totalminustime_starttransfer— body download. It grows with page weight and shrinks when compression is enabled.
A single measurement means nothing. Take a median across a series:
# ten TTFB samples, sorted, with the median at the bottom
for i in $(seq 1 10); do
curl -s -o /dev/null -w "%{time_starttransfer}\n" https://example.com/
done | sort -n | awk '{v[NR]=$1} END {
printf "min %.3f median %.3f max %.3f\n", v[1],
(NR%2 ? v[(NR+1)/2] : (v[NR/2]+v[NR/2+1])/2), v[NR] }'
Checking compression and cache headers for a specific asset. You must explicitly request compression via the Accept-Encoding header — without it the server will honestly return an uncompressed file and you will conclude compression is off:
# response headers only, for a CSS file
curl -s -o /dev/null -D - \
-H 'Accept-Encoding: br, gzip' \
https://example.com/assets/app.css \
| grep -i -E 'content-encoding|content-length|content-type|cache-control|etag|last-modified|vary|age|x-cache|cf-cache-status'
# compare the compressed and uncompressed size
curl -s -o /dev/null -w 'plain %{size_download} bytes\n' \
https://example.com/assets/app.css
curl -s -o /dev/null -w 'brotli %{size_download} bytes\n' \
-H 'Accept-Encoding: br' https://example.com/assets/app.css
What to check in the headers: a content-encoding of br or gzip on every text type; a cache-control with a long max-age and immutable for hashed static assets; a vary that mentions Accept-Encoding, otherwise a proxy may hand a compressed response to a client that cannot decode it. A quick way to inspect all of this is the HTTP header checker.
Finally, a lab run does not have to start with a button in the browser. Lighthouse ships as a CLI tool and drops into CI:
# install (requires Node.js and an installed Chrome)
npm install -g lighthouse
# default mobile run, HTML report
lighthouse https://example.com/ \
--output html --output-path ./report-mobile.html \
--chrome-flags="--headless --no-sandbox" --quiet
# desktop profile, no CPU throttling
lighthouse https://example.com/ \
--preset desktop \
--output json --output-path ./report-desktop.json \
--chrome-flags="--headless" --quiet
# five runs in a row so you can take the median score
for i in 1 2 3 4 5; do
lighthouse https://example.com/ --quiet --chrome-flags="--headless" \
--output json --output-path "./run-$i.json"
node -e "const r=require('./run-'+process.argv[1]+'.json');
console.log(Math.round(r.categories.performance.score*100))" "$i"
done
Limitation. curl does not execute JavaScript, build a DOM or paint pixels. It answers "does the server deliver bytes quickly" and says nothing about LCP, CLS or INP. Do not substitute a terminal measurement for a browser one — they answer different questions.
Test location, CDNs and why the measurement point changes the numbers
Site speed is not absolute: it is measured from a specific point on the planet. Between Western Europe and the US East Coast there is roughly 80–100 ms one way; across the Pacific, considerably more. Every network handshake multiplies that latency.
Consequences:
- PageSpeed Insights runs the lab test from Google infrastructure, not from your city. For a site with a regionally concentrated audience the lab half of PSI systematically shows a different network picture than visitors see. The field half is still correct: it was collected wherever the users live.
- WebPageTest and GTmetrix let you pick the test region. That is their strength: choose the location closest to your real audience and then freeze it forever so the history stays comparable.
- A CDN turns one site into many different sites. A visitor in Singapore and a visitor in Berlin hit different edge nodes with different cache states. A miss on a distant node means a trip to the origin across half the world.
- The first run after a deploy is almost always slow because the CDN cache is empty. Do not draw conclusions from it.
You can test a specific edge node and separate it from the origin by overriding name resolution:
# measure through a specific IP (a CDN edge node or the origin itself)
curl -s -o /dev/null \
-w "ttfb %{time_starttransfer}s total %{time_total}s\n" \
--resolve example.com:443:203.0.113.10 \
https://example.com/
# show whether the request hit the edge cache
curl -s -o /dev/null -D - https://example.com/assets/app.js \
| grep -i -E 'age|x-cache|cf-cache-status|x-served-by|server-timing'
A non-zero age header and a cache hit status mean the response came from an edge node. Zeros and constant misses mean caching is misconfigured — in that case the CDN adds a hop and makes things worse.
Cadence: a trend and a regression alert beat a one-off run
A one-off speed check is nearly useless. Run-to-run variance is often larger than the effect of a careful fix, and degradation rarely arrives as a single collapse — it comes as a dozen small steps: a new chat script, one more pixel, a "temporary" banner, an image uploaded by marketing without compression.
A workable setup looks like this:
- Scheduled synthetic runs. The same page, the same profile, several times a day, with history retained. The goal is the trend, not the absolute number.
- Alert on change, not on a threshold. A rule of "LCP above 2.5 s" will either stay silent forever or scream forever. Far more useful: "the daily median got 20% worse than last week".
- A performance budget. Limits fixed in advance: page weight, request count, JavaScript size, TBT. Breaching the budget fails the CI build — cheaper than fixing it after release.
- A lab run on every pull request. The Lighthouse CLI above runs in the pipeline against a preview environment. Compare against the base branch, not against an absolute threshold.
- RUM as the source of truth. Synthetic monitoring catches regressions quickly; field data confirms the regression is real for humans. How to set that up is covered in our real user monitoring guide.
- Annotate releases. Mark deploys on the metric chart. Then "when did it break" becomes "which release did it", and that takes a minute to answer.
In practice. Create a separate monitor for each page type: home, category, product, article, checkout. They degrade independently, and a site-wide average hides a collapse on your most profitable page.

What to do with the results
A tool's report is a list of symptoms. Below is a short route from symptom to cause to the guide that covers it. The order is deliberate: cheapest fixes first.
- High TTFB on a light page → the server, the database, a missing cache, a CDN miss. Check with
curl -w, then read server load troubleshooting. - Poor LCP where the LCP element is an image → format, dimensions, fetch priority. Covered in web image optimization.
- Heavy text assets → compression is off, or not everything is compressed. See gzip and brotli.
- High TBT and poor INP → JavaScript. What to actually do is in our breakdown of INP as a Core Web Vital.
- Unstable CLS → media dimensions, late banners, font swap. Background in the Core Web Vitals guide.
- Everything is bad and you do not know where to start → the systematic checklist in website speed optimization; if the site got slow suddenly, read why a website is slow to load and how to fix it.
- You need the full metric and threshold reference → the Web Vitals guide.
How to check your site speed with enterno.io
- Website speed test — a lab run broken down by metric with a list of issues. Entry point: do three to five runs and take the median.
- HTTP header checker — is compression on, is
cache-controlcorrect, are there redundant redirects, is static served without caching. - HAR file analysis — export the waterfall from DevTools and analyse it request by request: what blocks rendering, where the long chains are, what weighs the most.
- Page screenshot — how the page looks to an outside observer while loading; useful for catching invisible text caused by font loading and an empty first viewport.
- Website monitoring — scheduled recurring checks and alerts when a metric degrades after a release. Without this step everything above becomes a one-off ritual.
FAQ
Is an internet speed test the same as a website speed test?
No — these are two different measurements and people confuse them constantly. An internet speed test (including the branded checkers offered by ISPs) measures the bandwidth of your connection to a nearby provider server: how many megabits per second and what the ping is. It knows nothing about a specific website. A website speed test measures how quickly that site's server responds and how quickly the page renders in a browser. If every site is slow for you, you need a connection speed test. If one specific site is slow while your internet is fine, you need a website speed test.
Why does PageSpeed Insights show 95 while the site still feels slow?
Most likely you are looking at a lab score while the complaints are about responsiveness. The Lighthouse score does not include INP — the metric for reacting to user input. Heavy event handlers and long JavaScript tasks can make an interface sticky with an excellent score. The other option: you are benchmarking a light home page while the catalogue or account area is the slow part. Open the field section of the report — if INP is red there, it is JavaScript.
Why do two consecutive runs give different scores?
That is normal variance. It comes from load on the machine running the test, simulated throttling, banner rotation and A/B tests on your own site, CDN cache state and the behaviour of third-party hosts. A 5–15 point difference between runs of an unchanged page is expected. Do at least five runs and take the median rather than the last value.
Do I still need to improve FID?
No. FID was retired from Core Web Vitals in spring 2024 and replaced by INP. FID only measured the delay before the first interaction started being processed, and it was far too lenient: a page could have an excellent FID and still stutter on every click. INP looks at the whole interaction and reports the worst case of the visit. If your checklist or plugin still talks about FID, it is out of date.
What load time counts as normal?
It is more accurate to talk about specific metrics than about "load time". Reference points: LCP up to 2.5 s, INP up to 200 ms, CLS up to 0.1, TTFB up to 0.8 s — at the 75th percentile of real loads, evaluated separately for mobile and desktop. A single figure such as "a page should load in three seconds" means nothing: it does not say how it was measured, on what device, or up to which event.
Do I need a paid speed testing tool?
For a one-off diagnosis, no: the free capabilities of PageSpeed Insights, Lighthouse in DevTools and WebPageTest are more than enough. People usually start paying for two things: cadence (scheduled runs, long history, alerting) and field data on their own audience that public datasets do not cover. If you are doing a one-time optimization, free tools suffice; if you maintain a site continuously, you need monitoring.
Can I judge a contractor by the score?
Carefully. A score is easy to game: disable third-party scripts for the testing agent, serve a lighter version based on User-Agent, hide heavy blocks behind an interaction. Verify with field Core Web Vitals in Search Console and with your own RUM — those cannot be gamed because they are captured from real users. Ask for a month of LCP and INP data, not a screenshot of a coloured circle.
Website speed testing checklist
- You separate lab and field data and know which question each source answers.
- You look at LCP, INP and CLS rather than the headline score, and you know INP replaced FID.
- You run at least five times and take the median, not the last value.
- The measurement profile is frozen: device, throttling, region, one identical URL.
- Mobile and desktop results are never mixed.
- Runs use a cold cache, a clean browser profile, a production build, and no logged-in admin session.
- TTFB is measured separately with
curl -w, and you separate network latency from response generation time. - You verified that text assets are served compressed and that static files carry a correct
cache-control. - The test region matches where your audience actually is; for a CDN-backed site you confirmed edge cache hits.
- Recurring monitoring is in place with alerts on degradation relative to last week, not on an absolute threshold.
- You monitor every key page type, not just the home page.
- Results are annotated with releases so a regression takes a minute to attribute, not a day.