How to Score 100 on Google PageSpeed Insights (and Pass Core Web Vitals)
A 100 on PageSpeed Insights means every lab metric in Lighthouse's mobile test lands in the top band: content painted fast, almost no main-thread blocking and no layout shifts. It is achievable for most content sites by shipping less JavaScript, serving a small, prioritised hero image, self-hosting fonts and reserving space for everything that loads late. What matters more for users and for Google Search, though, is the field data above the score: the Core Web Vitals LCP, INP and CLS measured on real visits.
Two kinds of data on one page
PageSpeed Insights shows two different things, and confusing them causes most frustration:
| Field data ("Discover what your real users are experiencing") | Lab data (the 0–100 score) | |
|---|---|---|
| Source | Chrome User Experience Report (CrUX): real Chrome users over the previous 28 days | One Lighthouse run on Google's servers, emulating a mid-range phone on a throttled connection |
| Metrics | LCP, INP, CLS (the Core Web Vitals), plus FCP and TTFB | FCP, Speed Index, LCP, Total Blocking Time, CLS |
| Assessed at | 75th percentile of visits | A single page load |
| Updates | Slowly (rolling 28-day window) | Every run, and varies a few points between runs |
Interaction to Next Paint (INP), which replaced First Input Delay as a Core Web Vital in March 2024, needs real user interactions, so it appears only in field data. In the lab, Total Blocking Time is its closest proxy: a page whose main thread is rarely blocked usually responds quickly to taps.
Core Web Vitals thresholds
| Metric | Measures | Good | Poor |
|---|---|---|---|
| Largest Contentful Paint (LCP) | When the main content appears | ≤ 2.5 s | > 4.0 s |
| Interaction to Next Paint (INP) | Delay between an interaction and the next frame | ≤ 200 ms | > 500 ms |
| Cumulative Layout Shift (CLS) | Unexpected movement of content | ≤ 0.1 | > 0.25 |
A page passes when all three are "good" at the 75th percentile. Low-traffic pages may have no field data of their own; PageSpeed Insights then falls back to origin-level data or shows nothing.
How the lab score is calculated
Lighthouse converts each metric to a 0–100 score on a curve derived from real-world site data, then takes a weighted average:
| Metric | Weight |
|---|---|
| Total Blocking Time | 30% |
| Largest Contentful Paint | 25% |
| Cumulative Layout Shift | 25% |
| First Contentful Paint | 10% |
| Speed Index | 10% |
The curves are steep near the top: going from 95 to 100 requires mobile LCP well under 2 seconds and TBT close to zero. Because 55% of the weight is TBT plus LCP, those are where to start. The "Opportunities" and "Diagnostics" (or "Insights") list below the score names the specific resources responsible.
Fixing LCP: make the main content arrive first
- Find the LCP element. The report names it; on most pages it is the hero image or the first heading.
- Serve the hero image small and modern. Use WebP or AVIF, sized for the viewport with
srcsetandsizes, so phones do not download desktop images. - Prioritise it. Put the image in the HTML (not set by JavaScript or as a CSS background), add
fetchpriority="high", and neverloading="lazy"on the LCP image. Lazy-load images further down instead. - Cut server response time. Cache HTML at the edge or serve static pages. A slow Time to First Byte delays everything after it.
- Remove render-blocking resources. Inline the small amount of CSS needed for the first screen, load the rest normally, and add
deferto scripts.
<img src="/img/hero-960.webp"
srcset="/img/hero-640.webp 640w, /img/hero-960.webp 960w"
sizes="(max-width: 640px) 100vw, 960px"
width="960" height="640" alt="Product on a workbench"
fetchpriority="high">
Fixing TBT and INP: give the main thread back
- Ship less JavaScript. Audit third-party scripts first (chat widgets, tag managers, A/B testing, social embeds); each one runs on the user's main thread. Remove what nobody uses, and load the rest after interaction or on demand.
- Split bundles. Load code per route and import heavy components dynamically.
- Break up long tasks. Any task over 50 ms blocks input. Yield to the browser in long loops, for example with
await scheduler.yield()where supported or asetTimeoutfallback. - Keep event handlers light. Update the UI first, then do expensive work. Debounce input handlers and avoid forcing layout in loops.
Fixing CLS: reserve space
- Give every image and video
widthandheightattributes (or a CSSaspect-ratio) so the browser reserves the box before the file arrives. - Reserve fixed-size slots for ads, embeds and cookie banners, or overlay banners instead of pushing content down.
- Self-host web fonts, preload the one used above the fold, and use
font-display: swapwith a fallback font whose metrics are adjusted (size-adjust) to limit reflow when the web font arrives. - Never insert content above existing content unless the user asked for it.
Measure it yourself
Run Lighthouse locally or in CI to catch regressions before they reach users, and query PageSpeed Insights programmatically for many URLs:
# Lighthouse CLI, mobile emulation by default
npx lighthouse https://example.com/ --only-categories=performance \
--output=json --output-path=./report.json --quiet
# PageSpeed Insights API (an API key is recommended for regular use)
curl -s "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://example.com/&strategy=mobile&category=performance"
For field data on your own site, the open-source web-vitals JavaScript library reports LCP, INP and CLS from real visits to your analytics endpoint, with attribution data showing which element or interaction was slow. If you script the PageSpeed API in Python, urllib3 handles the requests and retries nicely.
Is a perfect 100 worth chasing?
The lab score is a diagnostic, not a ranking factor in itself. Google Search uses Core Web Vitals field data as one signal among many, and relevance still dominates. A site with a lab score of 92 and all-green field data is in better shape than one that scores 100 on an empty test page but stutters for real users on mid-range phones. Aim for green Core Web Vitals on your important templates, keep the lab score high as a guard against regressions, and treat the last few points as a bonus. The same principle (measure real users, fix the biggest problem first) applies to the accessibility and privacy checks in the guide to building a medical practice website.
Summary
PageSpeed Insights combines CrUX field data (LCP, INP, CLS at the 75th percentile) with a Lighthouse lab score weighted towards TBT, LCP and CLS. Reach the top of both by prioritising a small hero image, cutting and deferring JavaScript, breaking up long tasks, self-hosting fonts and reserving space for late content. Then monitor real-user metrics so the score reflects what visitors actually experience.
Sources
- web.dev, Web Vitals and Interaction to Next Paint (INP).
- Chrome for Developers, Lighthouse performance scoring.
- Google for Developers, About PageSpeed Insights.
WebBuilding a medical practice website
Accessibility, tracking pixels and HIPAA, safe forms, structured data and local search for a clinic site.
WebWooCommerce coupons
How WooCommerce coupons work: discount types, restrictions, usage limits, WC_Coupon in PHP, URL coupons, common problems and when a coupon plugin helps.
WebWhat is a CRUD application?
CRUD means Create, Read, Update, Delete. See how it maps to SQL and REST HTTP methods, with a runnable Python example, validation, soft deletes and idempotency.