Schedars · Resource
Web Performance Audit Checklist
The audit checklist we run to keep Lighthouse 95+ in production for 18+ months on client sites. Five metrics, five phases, 50+ concrete checks. Free, printable, share with your team.
TL;DR
LCP < 2.5s, CLS < 0.1, INP < 200ms at P75 mobile field-data is what ranks. Lab data (Lighthouse) is a regression detector, not the source of truth. Optimize against CrUX + Web Vitals JS, not Lighthouse score.
The five metrics — what and why
Largest Contentful Paint
How fast the biggest above-the-fold element renders. Measured at P75 mobile field-data via CrUX. The single most-weighted Core Web Vital for ranking.
Cumulative Layout Shift
How much visible content jumps around during load. Hurts perceived quality and accidentally clicks the wrong button. Common causes: images without dimensions, late-loading ads.
Interaction to Next Paint
How responsive the page feels when users tap/click/type. Replaced FID in 2024. P75 mobile across all interactions, not just first one. Killed by long-running JS in event handlers.
Time to First Byte
How fast the server starts sending the response. Not a ranking factor directly, but correlates with LCP. Measured server-side performance + edge caching effectiveness.
First Contentful Paint
How fast the first piece of DOM renders. Lighthouse uses it; CrUX still tracks it. Not a current ranking factor, but a useful debug metric — if FCP is bad, LCP is doomed.
Checklist — 50+ items
☐ untouched · ☐→☑ shipped · prefix with date when done.
Measurement setup
- Install Web Vitals JS library reporting to analytics (PostHog, Sentry, Vercel)
- Wire LCP / CLS / INP / TTFB / FCP into a dashboard with P75 mobile cuts
- Connect PageSpeed Insights to Search Console — surfaces field-data CrUX cuts
- Add Lighthouse CI to PR pipeline with thresholds blocking merge below targets
- Add size-limit (or bundlesize) on JS bundles per route — blocks bloat at PR-level
- Set up alerts for P75 mobile threshold breaches in real-user data
LCP — fix the biggest above-the-fold element
- Identify the LCP element via Lighthouse / PageSpeed / Web Vitals extension
- If LCP is an image: serve in WebP/AVIF, set explicit width and height attributes
- Add fetchpriority="high" to the LCP image (huge win, 200–500ms typically)
- Add <link rel="preload" as="image"> for the LCP image in <head>
- Self-host fonts; do not load Google Fonts CDN (TTFB hit + extra hop)
- Preload only the font face used in LCP text — typically one weight of one face
- font-display: swap on every @font-face declaration
- No render-blocking JS in <head>: defer or async every script that can wait
- Inline critical CSS for above-the-fold; async-load the rest
- Reduce TTFB: ensure server responds < 600ms via CDN edge caching
- Avoid client-side framework hydration before LCP — hydrate after paint where possible
- Image dimensions match display: do not serve a 4000px image into a 800px slot
CLS — eliminate layout shifts
- Always specify width and height on every <img> and <video> element
- Reserve space for ads, embeds, iframes — fixed min-height containers
- Avoid font swap shift: preload critical fonts, use size-adjust to match metrics
- Don't inject content above existing content unless triggered by user action
- Animations: use transform and opacity ONLY (not properties triggering layout)
- CSS aspect-ratio for media containers (e.g. "aspect-ratio: 16 / 9")
- Sticky elements (cookie banner, headers): position from start, not after JS loads
- Skeleton screens for dynamic content blocks instead of empty space
INP — make interactions feel instant
- Audit long tasks in the Performance panel — anything > 50ms blocks INP
- Break up long synchronous JS: scheduler.yield() or setTimeout(0)
- Defer non-critical JS execution: IntersectionObserver, idleCallback, web workers
- Avoid React state updates in event handlers without memoization
- Use Server Components / Astro islands to ship less client JS in the first place
- Audit third-party scripts: chat widgets, analytics, ad scripts — #1 INP killer
- Async-load heavy libraries (charts, maps, video players) only when needed
- On Next.js: prefer Server Components; Client Components only for interactivity
Image discipline
- All images go through Next/Image, Astro Image, or equivalent (no raw <img>)
- WebP/AVIF served with appropriate fallbacks
- Lazy-load below-the-fold images by default (loading="lazy")
- Hero/LCP image preloaded with fetchpriority="high"
- srcset + sizes set correctly so browser picks right resolution
- Image weight check in CI — flag any image > 200KB at PR level
Font discipline
- Self-hosted fonts (no third-party CDN that sets cookies or adds DNS lookup)
- font-display: swap (avoid blocking text rendering)
- Preload only the font used in LCP text — typically 1 face × 1 weight
- Variable fonts where they make sense (one file, multiple weights)
- Subset fonts to needed glyphs (Latin + your audience's scripts) — saves 50-200KB
- size-adjust + ascent-override to minimize CLS during font swap
Third-party script discipline
- Documented list of allowed third-party scripts with justification per entry
- New script requires PR with perf impact measurement
- Async-load every third-party script (chat, analytics, ad pixels)
- No inline JS in <head> from third parties
- Chat widgets: lazy-mount only after user intent (scroll past hero, etc.)
- Cap on total third-party script count — over 5 = audit and cut
Build + deploy
- Code splitting per route — no monolithic bundle for the whole app
- Tree-shake aggressively: remove unused exports from dependencies
- Compress with Brotli on production responses (better than gzip)
- CDN edge caching with appropriate Cache-Control headers
- Preconnect / dns-prefetch for cross-origin resources used early
- HTTP/2 or HTTP/3 served by your CDN (Vercel, Cloudflare default to it)
Continuous monitoring
- Quarterly manual audit: WebPageTest on a mid-tier mobile profile
- Weekly P75 mobile CrUX review in dashboard
- Lighthouse CI in pipeline blocks PRs below thresholds (LCP 2.5s / CLS 0.1 / INP 200ms / score 95)
- Visual regression testing via Playwright on top-5 pages
- Real Device Lighthouse runs (WebPageTest moto-G6 throttling) for mid-tier mobile profile
- Postmortem doc when a regression slips into production
Need a performance audit?
We'll run this checklist on your site and send back a prioritized fix list with estimated impact. Usually 1–2 days of work.