SEO Kickoff

Page Speed Optimisation: A Developer's Checklist for Sub-2-Second Load Times

10 min read
Page Speed Optimisation: A Developer's Checklist for Sub-2-Second Load Times

A 1-second delay in page load time reduces conversions by 7% and increases bounce rate significantly. Google uses page speed as a ranking factor through Core Web Vitals. Optimising for speed is simultaneously an SEO, UX, and revenue decision.


Establish a Baseline First

Before touching anything, measure:

  1. PageSpeed Insights — lab + field data, actionable diagnostics
  2. WebPageTest — detailed waterfall, time to first byte, filmstrip
  3. Chrome DevTools → Network tab — see exactly what loads and when

Record your current scores. Every optimisation should be validated against these baselines.

Target metrics:

  • TTFB (Time to First Byte): < 200 ms
  • LCP (Largest Contentful Paint): < 2.5 s
  • TBT (Total Blocking Time): < 200 ms
  • CLS (Cumulative Layout Shift): < 0.1

Layer 1: Server and Network

This is the highest-leverage layer. Server improvements help every single page.

Hosting Quality

Shared hosting TTFB is typically 400–800 ms. VPS or managed hosting: 50–150 ms. Cloud edge (Cloudflare Workers, Vercel Edge): 20–50 ms.

If your TTFB is over 400 ms, switch hosts before optimising anything else.

Content Delivery Network (CDN)

A CDN caches your pages at edge nodes around the world, delivering them from a server close to each visitor.

Cloudflare (free tier available) is the easiest to set up:

  1. Add your domain to Cloudflare
  2. Update your nameservers
  3. Enable "Cache Everything" for static pages

For dynamic sites, use edge caching with cache-control headers:

Cache-Control: public, max-age=3600, s-maxage=86400, stale-while-revalidate=604800

HTTP/2 and HTTP/3

HTTP/2 allows multiplexed requests (multiple files over one connection). HTTP/3 uses QUIC for faster connection establishment. Both are enabled automatically on Cloudflare and modern hosts.

Verify: Chrome DevTools → Network → Protocol column should show h2 or h3.


Layer 2: Images

Images are typically the largest assets on any page. They're also the most optimisable.

Format

FormatUse ForSavings vs JPEG
WebPPhotos, complex graphics25–35%
AVIFPhotos where maximum compression matters40–50%
SVGIcons, logos, illustrationsN/A (vector)

Convert all JPEGs and PNGs to WebP as a minimum. Use AVIF for hero images where browser support allows.

Compression

  • Lossy compression at 75–85% quality is visually lossless for most images
  • Tools: Squoosh, Sharp (Node.js), ImageMagick, Cloudinary

Responsive Images

<img
  src="hero-800.webp"
  srcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
  sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 800px"
  alt="Hero image"
  width="800"
  height="450"
  loading="lazy"
/>

In Next.js, <Image> handles all of this automatically.

Priority Loading

The LCP image should never be lazy-loaded. Add fetchpriority="high" or Next.js priority prop:

<Image src="/hero.webp" alt="Hero" priority width={1200} height={630} />

Layer 3: CSS and Fonts

Remove Unused CSS

Unused CSS is loaded, parsed, and held in memory on every page. Tools to find it:

  • Chrome DevTools → Coverage tab → reload page → see unused bytes
  • PurgeCSS (build-time tool) — removes unused Tailwind/utility classes
  • Tailwind CSS — automatically purges unused classes in production

Critical CSS

Inline the CSS needed for above-the-fold content directly in <head>. Defer everything else:

<style>/* critical CSS here */</style>
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">

Web Fonts

Fonts block rendering if not loaded efficiently:

<!-- Preconnect to font provider -->
<link rel="preconnect" href="https://fonts.googleapis.com" />

<!-- Preload the specific font file -->
<link rel="preload" href="/fonts/inter-v13-latin-regular.woff2" as="font" type="font/woff2" crossorigin />

Use font-display: swap or optional:

  • swap: text shows in fallback font immediately, swaps when font loads (can cause CLS)
  • optional: uses system font if web font isn't cached (no CLS, slight inconsistency)

Self-hosting fonts eliminates the DNS lookup to external font providers.


Layer 4: JavaScript

JavaScript is the most expensive resource on the web — it must be downloaded, parsed, compiled, and executed before it affects the page.

Defer Non-Critical Scripts

<!-- Blocks rendering: bad -->
<script src="analytics.js"></script>

<!-- Runs after HTML parsed: good -->
<script src="analytics.js" defer></script>

<!-- Runs as soon as downloaded, doesn't block: good for independent scripts -->
<script src="widget.js" async></script>

Code Splitting

In Next.js App Router, code splitting is automatic per route. To split further:

import dynamic from "next/dynamic";

const HeavyComponent = dynamic(() => import("./HeavyComponent"), {
  loading: () => <Skeleton />,
});

Audit Third-Party Scripts

Each third-party script (analytics, chat, ads, heatmaps) adds 50–300 ms of main-thread work. Audit quarterly:

  1. Chrome DevTools → Performance → record page load
  2. Identify third-party domains in the Network waterfall
  3. Remove any scripts whose value doesn't justify the cost

Tree Shaking

Only import what you use:

// Bad: imports entire library
import _ from "lodash";

// Good: imports one function (~1/50th the size)
import debounce from "lodash/debounce";

Layer 5: Caching

Browser Caching

Set long cache lifetimes for static assets via Cache-Control headers:

# Static assets (images, fonts, JS, CSS with content hash in filename)
Cache-Control: public, max-age=31536000, immutable

# HTML pages
Cache-Control: public, max-age=0, must-revalidate

Content-hashed filenames (e.g., main.a3f5c2.js) allow infinite caching — the hash changes when content changes, forcing a fresh download.

Service Worker / PWA Caching

For returning visitors, a service worker can serve cached assets instantly, making repeat visits feel instant.

Next.js supports this via next-pwa or custom service workers.


Page Speed Optimisation Checklist

Server & Network

  • TTFB under 200 ms
  • CDN enabled
  • HTTP/2 or HTTP/3 active
  • Gzip / Brotli compression enabled

Images

  • All images in WebP or AVIF
  • Images compressed to < 200 KB each
  • Responsive srcset on all content images
  • LCP image not lazy-loaded; has fetchpriority="high"
  • Width and height attributes set on all <img> elements

CSS & Fonts

  • Unused CSS removed
  • Fonts preloaded and self-hosted
  • font-display: swap or optional set

JavaScript

  • Non-critical scripts deferred
  • Third-party scripts audited and minimised
  • Dynamic imports used for heavy components
  • No render-blocking scripts in <head>

Caching

  • Long-lived Cache-Control headers on static assets
  • Service worker caching for repeat visitors

Related Articles