Website speed optimisation checklist showing TTFB, LCP, TBT and their fix priority order
Web Diagnostics

How to Speed Up a Website — 10 Fixes in Priority Order

Sunny Pal Singh · · 8 min read

Every guide to speeding up websites lists the same 30 things. Lazy load images. Minify CSS. Use a CDN. The problem isn't the advice — it's the order. A site with a 3-second TTFB will not get meaningfully faster from image lazy loading. Fix sequence matters more than the fix list.

Most page speed optimisation guides treat all fixes as equal. They're not. Some fixes move your Lighthouse score by 20 points; others move it by 1. Some fixes matter for real users; others only show up in synthetic tests. Knowing the order — and why — is what separates a fast site from one that's had "optimisation work" done to it without results.

Key Takeaways

  • TTFB above 600ms is a root cause — it delays every other metric; fix server response first before touching frontend optimisation
  • LCP and TBT together account for 55% of the Lighthouse score — prioritise fixes that move these two
  • Image optimisation has the highest ROI for most sites: right format, right size, right loading attributes
  • Third-party scripts (chat widgets, A/B testing, tag managers) are the most common source of high TBT — audit them ruthlessly
  • Measuring before and after each change is essential — some "best practices" hurt specific architectures

Measure First — What You're Actually Trying to Fix

Before making any change, measure. The three numbers you need:

  1. TTFB — Time to First Byte. Measures server response time. Get this from the Page Speed Inspector or Chrome DevTools Network tab (the dark green "Waiting" bar on any request).
  2. LCP — Largest Contentful Paint. The render time of your hero image or main heading. Get this from Lighthouse or PageSpeed Insights.
  3. TBT — Total Blocking Time. Main thread blocked by long JavaScript tasks. Get this from Lighthouse.

The fixes below are ordered: TTFB → LCP → TBT → everything else. If your TTFB is above 600ms, start at fix 1 regardless of what else is slow.

Fix 1 — Server Response Time (TTFB)

TTFB is the time from request sent to first byte received. It includes DNS lookup, TCP handshake, TLS negotiation, server processing, and return trip. A TTFB above 600ms means your server is slow — and slow servers make every other metric worse.

Fixes in priority order:

  • Enable server-side caching — if every page request hits the database, add page caching (Redis, Varnish, or your CMS's built-in cache). Cached responses serve in 10–50ms; uncached database-generated pages take 500ms–3s+.
  • Move to a server closer to your users — server geography directly affects TTFB. A server in the US serving European users adds ~100ms of pure round-trip time. Use a CDN with edge nodes in your key markets, or move your origin server.
  • Upgrade server resources — if you're on shared hosting with constrained CPU/RAM, even an optimised app will have high TTFB under load.
  • Optimise slow database queries — identify N+1 queries and missing indexes. A single slow query can add 1–2 seconds to every page load.

Fix 2 — Enable Compression

If your server isn't serving compressed responses, you're sending 3–5× more data than necessary for HTML, CSS, and JavaScript.

curl -sI https://your-site.com | grep content-encoding
# Should return: content-encoding: gzip  or  content-encoding: br

Enable gzip or Brotli at the server level (Nginx, Apache) or CDN. For Node.js/Express: app.use(compression()). Most CDNs (Cloudflare, Fastly) enable compression automatically.

Fix 3 — Set Cache Headers on Static Assets

CSS, JS, fonts, and images should be cached for 1 year by the browser. Without cache headers, repeat visitors re-download these files on every page load.

Cache-Control: public, max-age=31536000, immutable

immutable tells the browser not to revalidate during the max-age period — no conditional requests. Use this with hashed filenames (e.g. main.a3f9c2.js) so updates get fresh URLs.

Fix 4 — Preload and Prioritise the LCP Image

The LCP element (usually the hero image) should start loading as early as possible. Two attributes work together:

<link rel="preload" as="image" href="/hero.webp">
<img src="/hero.webp" fetchpriority="high" alt="...">

Without fetchpriority="high", browsers give the hero image the same priority as below-fold images. Without rel="preload", the browser doesn't discover the image until it parses the HTML. Together, these two often reduce LCP by 300–800ms.

Do NOT add loading="lazy" to your LCP image. Lazy loading delays loading until the image is near the viewport — which for the hero image is always, so it defers the load unnecessarily. Use fetchpriority="high" and never combine it with loading="lazy".

Fix 5 — Serve Images in Modern Formats

JPEG and PNG are legacy formats. WebP is 25–35% smaller than JPEG at equivalent quality. AVIF is 50% smaller than JPEG but with slightly lower browser support (95%+ as of 2026).

<picture>
  <source srcset="hero.avif" type="image/avif">
  <source srcset="hero.webp" type="image/webp">
  <img src="hero.jpg" alt="..." width="1200" height="630">
</picture>

Always include width and height attributes — these prevent CLS by reserving space before the image loads.

Fix 6 — Lazy Load Below-Fold Images

Images that aren't visible on initial load should use loading="lazy":

<img src="below-fold.webp" loading="lazy" width="800" height="400" alt="...">

The browser defers loading these until they're near the viewport. On image-heavy pages, this can save 500KB–2MB of initial load data. The exception: never lazy-load the hero image or any above-fold image.

Fix 7 — Remove Render-Blocking Scripts

Scripts without defer or async block HTML parsing — the browser stops building the page until the script downloads and executes.

<!-- Blocking — bad -->
<script src="analytics.js"></script>

<!-- Non-blocking — good -->
<script src="analytics.js" defer></script>

Use defer for scripts that need the DOM (most scripts). Use async for independent scripts that don't need the DOM or other scripts. Never put analytics or marketing scripts in <head> without defer/async.

Fix 8 — Audit Third-Party Scripts

Third-party scripts are the most common source of high TBT. Every chat widget, A/B testing tool, tag manager, and social embed adds main-thread blocking time.

Audit process: run Lighthouse → View Treemap or Coverage tab to see which scripts take the most time. Ask for each: does this actually convert? What does it cost in TBT?

Cut ruthlessly. A 50ms improvement in TBT from removing an unused script is worth more than hours of manual JavaScript optimisation.

Script type Typical TBT cost Recommendation
Live chat widget 150–400ms Load on interaction (click to open chat) not on page load
A/B testing tool 100–300ms Audit active tests — remove if not actively used
Tag manager (GTM) 50–200ms depending on tags Audit tags — remove unused; defer non-critical tags
Social embeds 100–500ms Replace with static thumbnails; load iframe on click

Fix 9 — Use a CDN for Static Assets

A CDN serves your CSS, JS, images, and fonts from edge nodes close to the user. Without a CDN, every asset request travels to your origin server.

Cloudflare free tier works for most sites. For static site hosting: Vercel, Netlify, and Cloudflare Pages include CDN by default. For self-hosted sites: Cloudflare proxying requires just a nameserver change and zero code changes.

Fix 10 — Reduce DOM Size

Large DOMs (5000+ nodes) slow layout, style recalculation, and JavaScript queries. Lighthouse flags DOM size as an audit item above 1500 nodes. Common sources: infinite scroll without virtualisation, deeply nested CSS frameworks, massive navigation menus built in HTML.

Fixes: virtualise long lists (render only visible items), flatten HTML structure, lazy-render off-screen sections.

Measuring Improvement

After each fix:

  1. Run Lighthouse 3 times, take the median score — single runs have natural variance of ±5–10 points
  2. Check the Page Speed Inspector for TTFB before and after server-side changes
  3. Check CrUX field data in PageSpeed Insights — it updates based on real user data, allow 2–4 weeks for changes to appear
Score variance is normal. Lighthouse uses CPU throttling and network simulation that vary slightly between runs. Don't chase single-point improvements — work on changes that move the score by 10+ points. A score of 85 on a real device is better than 92 in a synthetic test.

For a deeper look at how Lighthouse weights each metric, see the page speed score guide — it covers LCP, TBT, CLS, and FCP weights and what each one means in practice.

Measure Your Site's Current Speed

Free, no signup. The Page Speed Inspector measures TTFB, DNS, TCP, TLS, compression, cache headers, and 12+ HTML checks — giving you the full picture before you start optimising.

Try the Page Speed Inspector Free →
SP

Sunny Pal Singh

Fellow · Technical Director — AI Infrastructure, Cloud Orchestration & Network Automation

Sunny is a Fellow and Technical Director specialising in AI infrastructure, cloud orchestration, and network automation. With hands-on depth across AWS, Azure, GCP, Red Hat OpenStack, and OpenShift, he leads high-performing teams of architects and engineers building transformative solutions at scale. He built ByteWaveNetwork to bring the same engineering rigour to everyday web tooling.

Affiliate disclosure: Some links on this page may be affiliate links. We only mention tools we've personally used and have an honest opinion about. Affiliate revenue helps keep ByteWaveNetwork's tools free and maintained. We are not paid by any of the tools compared in this article for favorable coverage.

Choose design