Home Journal Performance

Performance

What actually makes a website fast

I audited this site and wrote down every number. One change was worth 844 milliseconds. Two textbook optimizations made it slower. Here's the difference.

Most website speed advice is folklore — a list of things that sound sensible, repeated until nobody checks them. The only way to know what makes a particular site fast is to change one thing, measure, and be willing to undo it.

So here's an honest audit, on this site, with the numbers I actually recorded. It started at 85 on mobile and finished at 97. What's interesting isn't the twelve points. It's which changes earned them.

First: measure the right thing

Lighthouse gives you one score, but it's assembled from five metrics with very different weights. Before optimizing anything, find out which one you're actually failing.

  • Largest Contentful Paint (LCP) — when the biggest thing on screen finishes rendering. Weighted heaviest. Usually your headline or hero image.
  • Total Blocking Time (TBT) — how long JavaScript hogged the main thread. The second heaviest.
  • Cumulative Layout Shift (CLS) — how much content jumped around while loading.
  • First Contentful Paint (FCP) — when anything at all appears.
  • Speed Index — how quickly the visible area fills in.

On this site, TBT and CLS were already zero. That's more than half the available score, perfect, before I touched anything — because the site has almost no JavaScript and every element has reserved space. Every point I was losing came from paint timing.

This is the step people skip If your problem is blocking JavaScript, self-hosting your fonts will do nothing. If your problem is paint timing, deferring scripts will do nothing. Optimizing the metric you're already passing is the most common way to spend a week and gain zero points.

The change that mattered: fonts

The single biggest problem was three lines of HTML that appear on almost every site on the internet:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=...">

That stylesheet is 1,280 bytes. It cost 844 milliseconds of render-blocking time.

The reason is the shape of the request, not its size. The browser has to fetch a stylesheet from fonts.googleapis.com, parse it, discover that it points at font files on a different origin, connect to fonts.gstatic.com, and fetch those. Three sequential round trips before a single word of text can render. On a throttled mobile connection, that's most of a second spent waiting for permission to start.

The fix is to serve the fonts from your own domain. Download the .woff2 files, drop them in a folder, declare them with @font-face, and delete the Google links.

Result: First Contentful Paint and Largest Contentful Paint both went from 3.1 seconds to 1.7 seconds. The score went from 85 to 97. That one change was the entire improvement.

Two details worth copying. Use the variable font files where they exist — four files replaced the nine separate weights the Google URL had been requesting. And set font-display: swap so text paints immediately in a fallback rather than waiting invisibly.

The changes that made it worse

This is the part that gets left out of most performance write-ups, and it's the part worth reading.

Preloading the fonts

Standard advice: once fonts are self-hosted, preload them so the browser starts fetching immediately instead of waiting to discover them in the CSS.

Measured: LCP went from 1.8s to 2.1s. Score dropped from 97 to 96.

Why: preload raises priority. On a bandwidth-limited connection, raising the fonts' priority means they compete with the stylesheet — and until the stylesheet arrives, nothing renders at all. I made the fonts win a race they didn't need to win, and the page paid for it. Reverted.

Inlining the CSS

Also standard advice: inline your stylesheet so there's no second request before first paint.

Measured: FCP went from 1.6–1.8s to 2.0–2.3s. Scores fell as low as 91.

Why: the HTML document is itself render-blocking. Inlining a 28KB stylesheet turned a 22KB page into a 50KB page, so the browser now waits longer for the document before it can even begin. It also destroyed the caching benefit — one shared stylesheet cached across every page became 28KB re-downloaded on every navigation. Reverted.

Both of these are genuinely good advice in the right context: a site whose LCP is a hero image, or a page with a tiny amount of critical CSS. Neither described this site. The advice wasn't wrong; it was wrong here, and the only way to know that was to measure.

The smaller wins

Three things worth about a point each, all cheap:

  • A forced reflow in the scroll handler. Reading window.scrollY directly inside a scroll listener forces the browser to recalculate layout synchronously, every event — 32ms of it. Moving the read inside requestAnimationFrame fixed it.
  • Fading in content that was already visible. Above-the-fold elements started at opacity: 0 and animated in over 650ms. Nobody perceives that as elegance on first load; they perceive it as a slow site. Elements already on screen now appear instantly, and the scroll animation runs only below the fold.
  • Heading order. Not a speed fix, but it took accessibility from 98 to 100 and cost five minutes.

How to do this yourself

Four rules, learned the hard way:

  • Run every test at least twice. Single runs vary by one to two points. I once spent twenty minutes chasing a "regression" that was my own broadband getting congested.
  • Change one thing at a time. Bundle three optimizations and you'll never know which one worked, or which one quietly cancelled out the others.
  • Compare back to back, not against memory. If your connection drifts between measurements, you're comparing two different worlds. Test A, then B, then A again.
  • Be willing to revert. Two of the changes here were textbook best practice and both made the site slower. The measurement is the authority, not the article you read.

The uncomfortable conclusion is that most speed work is deleting things rather than adding them. The 844ms didn't come from a clever technique. It came from removing three lines of HTML that everyone copies without thinking.

Want to know what's slowing yours down?

An audit finds the specific bottleneck on your site — not a generic checklist — with the numbers to back it.

Start a project