Website Spec
Performance Required Updated 2026-05-29

Compression (gzip, brotli, zstd)

Compress text responses with brotli where supported, gzip everywhere else. zstd is emerging. Don't compress already-compressed media.

What it is

HTTP content encoding compresses response bodies on the server and decompresses them in the browser. The client advertises supported algorithms in Accept-Encoding; the server picks one and announces it in Content-Encoding.

Request:
Accept-Encoding: br, gzip, zstd

Response:
Content-Encoding: br
Vary: Accept-Encoding

The common algorithms:

  • gzip — universally supported since the late 1990s. Good ratio, fast.
  • brotli (br) — Google, standardised as RFC 7932. 15–25% smaller than gzip for text. Supported by all modern browsers.
  • zstd — RFC 8878. Comparable ratio to brotli with faster decompression. Supported by Chrome 123+, Firefox 126+, Safari 17.4+.
  • deflate — avoid. Ambiguity between raw deflate and zlib wrapping has bitten implementations for decades.

Why it matters

Text resources — HTML, CSS, JavaScript, JSON, SVG, XML — compress to 20–30% of their original size. On a 200KB JavaScript bundle, that is 140KB saved per request, multiplied by every user. Compression is one of the cheapest performance wins available, and a Lighthouse audit failure if missing.

How to implement

Compress all text. HTML, CSS, JS, JSON, SVG, XML, plain text, and most fonts (WOFF2 is already compressed; WOFF is not).

Negotiate via Accept-Encoding. Most servers and CDNs do this automatically. Configure them to prefer brotli, fall back to gzip:

  • nginx: brotli on; brotli_static on; gzip on;
  • Apache: mod_brotli and mod_deflate.
  • Cloudflare, Fastly, Cloudfront: enable in the dashboard.

Pre-compress static assets. For files that don’t change (your bundled JS), compress at build time to maximum level (brotli quality 11, gzip level 9) and let the server serve the .br or .gz file directly. Runtime compression usually runs at level 5–6 for CPU reasons.

Set Vary: Accept-Encoding. Tells CDNs to keep a separate cache entry per encoding. Without it, gzip clients may receive a brotli body they can’t decode.

Don’t double-compress. Images (JPEG, PNG, WebP, AVIF), video, fonts (WOFF2), and zip files are already compressed. Re-encoding wastes CPU and often grows the file.

Common mistakes

  • Compression off entirely. Lighthouse “Enable text compression” fails immediately.
  • gzip only — a 20% saving left on the table for every brotli-capable browser.
  • Compressing images or video. Pure waste.
  • Missing Vary: Accept-Encoding behind a CDN. Cache poisoning waiting to happen.
  • Brotli at quality 11 on dynamic responses. The encode cost erases the wire saving.

Verification

  • curl -H "Accept-Encoding: br, gzip" -I https://example.com/main.css — check Content-Encoding.
  • DevTools → Network → response headers should show content-encoding: br.
  • Compare Network “Size” (over the wire) vs “Resources” (decompressed). The ratio shows compression is working.

Related topics

Sources & further reading

Search
esc close navigate open