Front-end tweaks get most of the attention, but many slow sites are slow before the first byte arrives. If the server takes 800 ms to respond, no amount of image optimisation fixes it. This checklist works from the server outward, in roughly the order of impact.

1. Measure first

  • Time to First Byte (TTFB) — how long the server takes to start responding:
curl -o /dev/null -s -w 'connect:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n' https://example.com/
  • Field data — Core Web Vitals from real users (Search Console, CrUX).
  • Lab tools — Lighthouse, WebPageTest (test from regions where your visitors are).

A good TTFB is under ~200–300 ms for cached pages; a high connect time points to distance, a high gap between tls and ttfb to slow application code.

2. Put the server near your users

Every round trip costs latency, and a page load needs several. Host where your audience is — see choosing a VPS location — and use a CDN for global audiences.

3. Modern protocols

  • HTTP/2 (and optionally HTTP/3) — multiplexed requests over one connection. See HTTP/2 and HTTP/3 in Nginx.
  • TLS 1.3 with session resumption — faster handshakes.
  • Keep-alive connections — enabled by default in Nginx.

4. Compression

Enable gzip and Brotli for HTML, CSS, JS, JSON and SVG — often a 70–80% size reduction. See Nginx compression.

5. Caching at every layer

Layer What it saves Guide
Browser cache Re-downloading assets Nginx browser caching
CDN / edge Distance and origin load CDN setup
Full-page cache Running PHP/app code at all Nginx FastCGI cache, Varnish
Object cache Repeated database queries Redis object cache
OPcache Recompiling PHP PHP OPcache
Database buffer Disk reads MySQL tuning

A full-page cache typically turns a 400 ms WordPress response into 10–30 ms.

6. Application and runtime

  • Enough workers (PHP-FPM, Gunicorn, PM2) for concurrent traffic, but not more than RAM allows — see PHP-FPM tuning.
  • Current runtime versions (PHP 8.x is markedly faster than 7.x).
  • Remove slow plugins/middleware; profile with tools like Query Monitor (WordPress), Xdebug or APM.
  • Move heavy work (emails, image processing, reports) to background jobs.

7. Database

  • Indexes on columns used in WHERE, JOIN and ORDER BY — see database indexing.
  • Slow query log to find the worst offenders — see MySQL slow query log.
  • Enough RAM for the working set.

8. Images and media

Images are usually the heaviest part of a page:

  • Modern formats: WebP or AVIF — see WebP and AVIF.
  • Correct dimensions (srcset), not 4000-pixel originals scaled down by CSS.
  • loading="lazy" for below-the-fold images; never lazy-load the main (LCP) image.
  • Video: stream from a platform or with proper encoding, don’t serve huge files inline.

See WebP and AVIF images.

9. Front-end delivery

  • Fewer, smaller JavaScript bundles; defer non-critical scripts.
  • Inline critical CSS or keep CSS small; avoid render-blocking third-party CSS.
  • Self-host fonts with font-display: swap, subset them, preload the main one.
  • Limit third-party tags (chat widgets, trackers, A/B testing) — often the biggest slowdown on modern sites.
  • rel="preconnect" for essential third-party origins.

10. Server resources

If CPU or RAM is saturated during normal traffic, tuning only goes so far. Check top, free -h and I/O wait during peaks — see CPU and RAM monitoring. Upgrade when monitoring shows sustained saturation.

Prioritise

  1. Page caching + compression + HTTP/2 (biggest wins, little effort).
  2. Location / CDN.
  3. Images.
  4. Database and application profiling.
  5. Front-end fine-tuning.

Related: Core Web Vitals from the server side, WordPress performance optimization and load testing with k6.

  • Performance
  • Web Development
  • Caching