Every site we ship goes through the same performance pass before launch. It is not glamorous work — most of it is deleting things — but it is the difference between a site that scores 96 on a Monday and a site that stays fast eighteen months later after marketing has added four tracking scripts. This is the actual checklist, in the order we run it, with what each item typically buys you.
The short version
- Images and third-party scripts cause the overwhelming majority of slow sites. Fix those two and you have fixed most of it.
- Optimise for field data (real users, Chrome UX Report) not just lab scores. A 100 in Lighthouse on your fibre connection proves very little.
- The three metrics that matter: LCP under 2.5s, INP under 200ms, CLS under 0.1.
- Reserve space for everything that loads late — images, ads, embeds, fonts. Unreserved space is where layout shift comes from.
- Performance is not a launch task. It regresses. Put a budget in CI or it will drift.
Step 0: measure properly, before you touch anything
Optimising without a baseline means you cannot tell improvement from luck. Before changing a single line:
- Run PageSpeed Insights on your three most important pages — usually home, a service or product page, and the contact page. Note both the lab score and the "real user experience" section, which is field data from actual visitors.
- Test on a throttled mobile connection. Chrome DevTools → Network → "Slow 4G", plus CPU throttling at 4×. This approximates the mid-range Android phone most of your visitors actually use.
- Look at the waterfall, not the score. The score is a summary; the waterfall tells you which request is blocking.
- Record the numbers somewhere. You will want them for the "before and after" conversation.
What the three Core Web Vitals actually measure
LCP (Largest Contentful Paint) — when the biggest visible element finishes rendering. Usually your hero image or headline. Target: under 2.5 seconds.
INP (Interaction to Next Paint) — how long the page takes to visibly respond to a tap or click. Replaced FID. Target: under 200ms.
CLS (Cumulative Layout Shift) — how much content jumps around while loading. Target: under 0.1.
Images — items 1 to 6
If you only do one section, do this one. On the sites we are asked to fix, images are the single largest cause of slow loading roughly seven times out of ten.
- Resize to the display size. A 4,000px photo displayed in a 600px card wastes 90% of its bytes. Export at roughly 2× the largest display width and no more.
- Serve WebP or AVIF. WebP typically saves 25–35% over an equivalent JPEG at the same visual quality; AVIF often more. Keep a JPEG fallback via
<picture>only if you must support very old browsers. - Set explicit
widthandheighton every image. This is the single highest-value CLS fix. Without dimensions the browser cannot reserve space, so everything below the image jumps when it arrives. - Lazy-load below-the-fold images with
loading="lazy"— and never lazy-load the hero image. Lazy-loading your LCP element actively delays it, and this mistake is remarkably common. - Preload the hero image with
<link rel="preload" as="image">so it starts downloading before the CSS finishes parsing. - Use responsive
srcsetso phones download a phone-sized file. A single desktop-sized image served to everyone is a mobile performance tax.
Fonts — items 7 to 9
- Self-host, or preconnect. A third-party font host means an extra DNS lookup, TLS handshake and round trip before any text can render. Self-hosting removes all three; if you must use a hosted font, add
<link rel="preconnect">. - Use
font-display: swapso text renders immediately in a fallback rather than staying invisible. Pair it with a fallback whose metrics are close to your web font — otherwise you trade invisible text for a visible reflow. - Cut the weights. Most sites load six weights and use three. Each weight is a separate file. Regular, semibold and bold cover nearly every design.
JavaScript and third parties — items 10 to 15
This is where sites silently degrade over time. Nobody removes a tracking script.
- Audit every third-party script. List them, and for each one name the person who will notice if it disappears. Scripts with no owner get deleted. We routinely remove three to five.
- Defer everything non-critical.
deferfor scripts that need the DOM,asyncfor genuinely independent ones. Nothing except a critical inline snippet should block parsing. - Load chat widgets and video embeds on interaction. A live-chat bundle can be 300KB+ and a YouTube iframe pulls in over a megabyte. Replace them with a lightweight placeholder that loads the real thing on click — the "facade" pattern. This is often the largest single win available on a marketing site.
- Split your bundles. If you are on a framework, code-split by route so a visitor to the homepage does not download the checkout logic.
- Remove unused CSS and JS. DevTools → Coverage shows exactly what went unused. A full icon font for six icons, or an entire UI library for two components, is pure waste — inline the SVGs instead.
- Watch your tag manager. One container tag can hide a dozen scripts. Audit inside it, not just the page source.
If you run ads on the site
Ad slots are the most common source of layout shift on content sites. Give every slot a fixed reserved height in CSS at each breakpoint, so the page does not jump when a creative loads. Also place slots so the largest one is not competing with your hero image for the first second of bandwidth.
Server and delivery — items 16 to 20
- Enable Brotli or gzip compression. Text assets compress by 70–80%. It is a one-line server change and it is still missing on a surprising number of live sites.
- Set long cache headers on static assets — a year for fingerprinted files — and use versioned filenames so you can still ship updates instantly.
- Put a CDN in front. If your server is in Karachi and your visitor is in Toronto, physics is your bottleneck. A CDN solves it for static assets and costs very little.
- Cache at the server. Page caching on WordPress, or a proper query and object cache on a custom app. Most slow "backend" pages are a database query executed on every request that could have been cached for five minutes.
- Check your TTFB. Above roughly 600ms and the problem is server-side — slow queries, no caching, or oversubscribed shared hosting — not front-end. Our hosting guide covers when to move up a tier.
The last four — items 21 to 24
- Inline critical CSS for above-the-fold content and load the rest asynchronously. Meaningful on content-heavy pages, fiddly to maintain, so we do it selectively rather than always.
- Reduce main-thread work. Long tasks are what wreck INP. Break up heavy work, avoid layout thrashing in scroll handlers, and debounce anything that fires on input.
- Avoid chained redirects. Each hop is a full round trip.
http://example.com→https://example.com→https://www.example.com→/homeis three wasted trips before anything renders. - Set a performance budget in CI. Fail the build if the JS bundle grows past a threshold or Lighthouse drops below a floor. Without this, the site regresses within two quarters — guaranteed.
If you are on WordPress
The same principles apply, with three additions that account for most WordPress slowness:
- Audit plugins ruthlessly. Every active plugin can enqueue its own CSS and JS on every page. We regularly find sites loading a slider library on pages with no slider. Deactivate, measure, repeat.
- Pick one caching plugin and configure it properly. Two caching plugins fighting each other is worse than none.
- Question your page builder. Heavy visual builders can add 200–400KB of CSS and JS before your content. Sometimes the right fix is a lean custom theme — which is a rebuild, so weigh it honestly against the traffic you have.
What this is actually worth
Two things, and it is worth being precise about both because the internet exaggerates them.
Conversion. The relationship between speed and conversion is real and consistently measured across large retail datasets, but it is not a fixed multiplier you can promise. What we can say from our own before/after data is that dropping mobile LCP from around 5 seconds to under 2.5 reliably reduces bounce on landing pages, and the effect is largest on paid traffic where visitors have no loyalty and no patience.
Search. Core Web Vitals are a genuine ranking signal, but a modest one — relevance and content quality dominate. Speed will not rescue a thin page. It will differentiate you from a competitor whose content is equally good and whose site takes six seconds to load. Treat it as a tiebreaker you should always win, not a growth strategy on its own.
Fast is a feature you only notice when it is missing. Nobody has ever emailed to say the site loaded quickly — but they do leave when it does not.
We run this pass on every web development project, and we also run it as standalone work on sites we did not build. If you want the specific list for your site rather than the general one, send us the URL and we will tell you what is costing you the most.