32 misconceptions

Don’t memorise rules.

Most performance advice started out true. Then the protocol, the browser or the way sites are built changed, and the rule stayed behind. Each card below is a claim you’ll hear, a one-line verdict and the reason. The links go to the experiment where you can see it for yourself.

Network

How bytes get to the browser, and how to need fewer of them.

Not universally true

“Browsers only load six resources at once.”

Six connections per origin is an HTTP/1.1-era habit. HTTP/2 and HTTP/3 multiplex many requests over one connection, and the real limits depend on protocol, origin, connection reuse and priorities.

Outdated

“More requests are always bad.”

On HTTP/2 and HTTP/3 a request is far cheaper than it was, and a cached request is free. Many small, separately cacheable files can beat one bundle that changes on every release.

Backwards

““no-cache” means the browser won’t cache it.”

It means “store it, but check with me before every reuse”. Use no-store if you truly want nothing kept.

Only what you can rename

“Cache everything for as long as possible.”

A long cache on a URL you can’t change is a promise you can’t take back. Fingerprint the assets and keep the HTML revalidating.

Not by itself

“A service worker makes my site faster.”

It makes repeat visits and bad connections better if you configure it well. A first visit gets nothing from it, and a worker that has to start before it answers can add delay.

Only for what never changes

“Cache-first is the fast option, so use it everywhere.”

Cache-first is right for fingerprinted assets. On a document or API response it builds a stale-forever trap.

Fixes one problem, creates another

“I’ll call skipWaiting() so updates always work.”

It skips the waiting, and can leave old pages running under a new worker that has deleted what they need.

It moves one request, and crowds the rest

“Preload makes everything faster.”

Preload starts a request sooner and ranks it higher. Everything else now has more to compete with for the same bandwidth. It helps where discovery is late, and overused it delays the resources that matter most.

Only the critical few

“I should preconnect to every third party.”

An unused connection is wasted work, and Chrome closes a preconnected connection that isn’t used within about 10 seconds. Preconnect where you’re certain, dns-prefetch for the rest.

It’s a hint

“A resource hint is a command.”

Browsers may ignore hints on slow or metered connections, in data-saver mode, or when they’ve already done what you asked.

Every miss is a wasted page load

“Just prerender everything and the site is instant.”

Prerender runs the whole page in the background. Speculate where intent is clear, like the link under the pointer.

Different gains, different costs

“Prefetch and prerender are the same thing.”

Prefetch fetches the document, not the page’s subresources. Prerender loads and renders the whole page in the background. Only prerender makes a navigation close to instant.

Until something disqualifies the page

“The back/forward cache is automatic, so there’s nothing to do.”

On desktop Chrome and Firefox one unload handler disqualifies the page, as can a forgotten open connection or a no-store response. Then the near-instant back button becomes a full reload.

Assets

What you send: images, fonts, JavaScript and everyone else’s scripts.

Not for what’s on screen

“Lazy loading everything is better.”

Lazy loading saves bytes by waiting, and waiting is the wrong thing for the hero. Above-the-fold images should load as early as possible.

Usually, not always

“A smaller file is always faster.”

A more aggressive format can cost more to decode. A smaller file discovered late, or behind a queue, still arrives late. Bytes are one of several costs.

It fixes invisible text only

“font-display: swap fixes font loading.”

The font still arrives late, and now the page jumps when it does. Fix the jump with a better fallback and the lateness by fetching less, sooner.

Earlier isn’t smaller

“Preload the font and the problem goes away.”

Preload moves the request earlier. It doesn’t shrink the file. With a heavy payload on a slow network a preloaded font is still late.

WOFF2 is enough

“I need WOFF, TTF and EOT for compatibility.”

Every current browser supports WOFF2. Extra formats are dead weight to maintain.

Bytes are the wrong unit

“It’s only 150 KB. That’s smaller than a photo.”

A photo’s cost is mostly download. A script’s cost is what happens after it arrives: parse, compile and run on the main thread, scaled by how slow the device is.

They stop blocking the parser, not the page

“I used async or defer, so scripts are free.”

The script still runs on the main thread once downloaded, and a long task blocks taps just the same.

Your laptop isn’t your user

“It’s fast on my laptop.”

Change the CPU from desktop to phone and the network doesn’t change, but main-thread time grows several times over.

Compression made the download cheap

“I compress my JavaScript, so it’s cheap.”

The browser decompresses and then parses the whole thing. Parse and compile cost didn’t move.

Look at what it loads next

“It’s a tiny script.”

The first file is rarely the cost. A tag manager of 90 KB can bring another 120 KB and a second burst of work.

Rendering

What the browser does with it once it has arrived.

It can’t know what the page looks like

“The browser should show the page as soon as the HTML arrives.”

The HTML says what is on the page; the CSS says how it looks, and layout depends on it. Painting early would mean painting the wrong thing and repainting.

It moves the bottleneck

“Make everything async and the render path is solved.”

Async and defer change who blocks the parser. CSS still blocks rendering, scripts still occupy the main thread, and images and fonts still arrive late.

Bytes are the wrong unit again

“CSS is just text, so it’s cheap. Images are the heavy part.”

A stylesheet blocks rendering, then costs main-thread time to parse and match against your elements, and again every time the DOM changes.

Usually not

“I should rewrite my selectors for speed.”

Cut unused CSS, shrink the DOM and scope class changes first. Selector rewrites tend to cost maintainability for gains you can’t measure.

It pays on long pages

“Put content-visibility: auto on everything.”

On a short page it adds complexity and layout jumps for nothing. The demo shows the saving only on a big page.

Measuring

Knowing whether any of it worked.

One simulated visit was fast

“A high Lighthouse score means my website is fast.”

A lab run is one visitor on a fixed profile. Real visits differ, and what happens after load shapes the experience. Lab scores, field data and perceived responsiveness can all disagree.

The average describes nobody

“Our average load time is 1.8 seconds.”

Visits are lopsided: many fast ones and a tail of very slow ones. The median and the 75th and 95th percentiles describe what people experience.

Repeatable isn’t representative

“Field data is noisy, so lab data is more reliable.”

Lab data reliably measures the wrong thing if conditions don’t match your visitors. The noise in field data is real people.

They’re already well served

“Optimise for the typical user.”

The same fix does far more for a low-end phone than for a desktop. Performance work pays off at the tail.