skip to content

Compare gzip, Brotli (br) and Zstandard (zstd) as HTTP content codings. When would you choose each?

level: middleimportance: should knowfreq 40%

answer

  1. gzip = universal floor, cheap, weakest ratio
  2. br = web dictionary, best text ratio, level 11 = build-time only
  3. zstd = near-br ratio, much faster encode + fastest decode
  4. static: precompress max level; dynamic: low level
  5. never compress JPEG/PNG/MP4/WOFF2

basics

~20 s

gzip is the universal baseline — modest ratio, fast, supported everywhere. Brotli compresses text best, especially precompressed at high levels, but is slow to compress at high settings. zstd compresses near-Brotli quality far faster, making it good for dynamic responses; browser support is the newest.

solid answer

~60 s

All three are lossless content codings; they differ in ratio-versus-CPU and in support. - **gzip** (DEFLATE) — the universal floor. Every client and intermediary handles it. Ratios on text are the weakest of the three, compression and decompression are fast and cheap. Always keep it as the fallback. - **br (Brotli)** — designed for the web, ships with a built-in dictionary of common web strings, so it beats gzip meaningfully on HTML/CSS/JS, often 15–25% smaller. Its top levels (10–11) are far too slow for per-request work but ideal for **precompressed static assets** built once at deploy time. For dynamic responses use low levels (4–6). - **zstd** — very wide speed/ratio curve. At comparable ratios to Brotli it compresses several times faster, and decompresses faster than both, which makes it the strongest choice for dynamic, per-request compression and for internal service-to-service traffic. Browser support is the most recent of the three, so it must be negotiated, never assumed. Practical policy: precompress static assets with Brotli-11 plus gzip; compress dynamic responses with zstd or low-level Brotli; always fall back to gzip.

code

http · 9 lines
http
GET /static/app.4f2c.js HTTP/1.1
Host: cdn.example.com
Accept-Encoding: gzip, br, zstd

HTTP/1.1 200 OK
Content-Type: text/javascript
Content-Encoding: br
Vary: Accept-Encoding
Cache-Control: public, max-age=31536000, immutable

go deeper

for a junior

Know the three names, that gzip is the safe universal default, and that Brotli usually produces smaller text.

for a middle

Explain the ratio-versus-CPU tradeoff, Brotli's web dictionary and level curve, and why static assets get maximum level while dynamic ones do not.

for a senior

Add operational specifics: preferred codings per path, minimum size thresholds, media-type allow-lists, zstd for internal traffic and dictionaries, and measuring on your own payloads.

for a principal

Set the estate-wide policy — which layer compresses, CPU budget versus egress cost, when the ratio gain stops justifying latency and complexity, and how new codings get rolled out safely.

## The three codings All three are lossless, general-purpose compressors offered as HTTP content codings via `Accept-Encoding`/`Content-Encoding`. Choosing between them is a ratio-versus-CPU-versus-support decision, and the right answer differs for static and dynamic content. ### gzip DEFLATE (LZ77 + Huffman) in a gzip container. Decades old, in every client, proxy, and CDN. Its ratio on text is the weakest of the three but its cost is low and predictable, and its 32 KB window limits how much redundancy it can exploit in large files. It is the **floor you never remove**: any client that speaks HTTP at all speaks gzip, and any corporate middlebox that mangles newer codings will pass gzip. ### Brotli (`br`) Built for the web. Two things distinguish it: - A **built-in static dictionary** of roughly 120 KB of common web substrings (`<!doctype html`, `function`, common English words). This is why it wins so decisively on small text files, where a general compressor has no history to learn from yet. - A much larger window (up to 16 MB) and 12 quality levels. The level curve is the operational point: level 11 gives the best ratio on the web but can be orders of magnitude slower than gzip to compress — completely unsuitable per request, and perfect at build time. Levels 4–5 are roughly gzip-speed with a better ratio, which is what reverse proxies use for dynamic responses. Decompression is fast and level-independent, so clients are never penalised for a high encode level. One constraint: Brotli is only offered over HTTPS by browsers. ### Zstandard (`zstd`) A modern compressor with an unusually wide, tunable speed/ratio curve (levels ~1–22, plus negative "fast" levels). Around Brotli-quality ratios it compresses substantially faster, and its **decompression is the fastest of the three**, which matters for constrained clients and high-throughput internal traffic. It also supports trained custom dictionaries, which is a real advantage for many small, structurally similar payloads such as JSON API responses. Its weakness is purely ecosystem age: it is the newest of the three in browsers and in intermediaries, so it must be genuinely negotiated. ## Static versus dynamic — the decision that matters **Static assets** (JS bundles, CSS, fonts-as-text, prerendered HTML) are compressed **once at build time** and served from disk. Compression CPU is paid once for millions of requests, so use the maximum: Brotli level 11, plus a gzip copy for the fallback. Adding a zstd copy is cheap and helps clients that prefer it. **Dynamic responses** (API JSON, personalised HTML) are compressed on every request, on the serving path, inside the user's latency budget. Use a fast level: zstd at a low level, or Brotli 4–5, or gzip. Brotli-11 on a hot dynamic endpoint is a classic self-inflicted latency and CPU incident. **Internal service-to-service traffic** is the clearest zstd case: both ends are yours, so support is not in question, and the fast decompression plus dictionary training pays off on repetitive payloads. ## What not to compress at all Already-compressed formats — JPEG, PNG, WebP, AVIF, MP4, ZIP, most fonts (WOFF2 is Brotli internally) — gain nothing and often grow slightly. Very small bodies (under roughly 1 KB) can be net-negative once framing overhead is counted. Both are normally handled by a size threshold plus a media-type allow-list in the proxy config. ## Negotiating between them The client offers what it supports and the server picks. Preference order in a typical config is `zstd > br > gzip` for dynamic and `br > zstd > gzip` for precompressed static (because the Brotli-11 artifact already exists). The chosen coding is declared in `Content-Encoding`, and `Vary: Accept-Encoding` must be set so each variant is cached separately. ## How to answer this well Avoid "Brotli is better than gzip" as a flat statement. The strong answer is: compression **level** matters more than compressor choice for latency; the static/dynamic split determines which level you can afford; gzip stays as the universally safe fallback; and you should measure on your own payloads, because ratios on minified JS, on prose HTML, and on repetitive JSON differ substantially.

  • Why is Brotli level 11 fine for static assets but a bad idea for dynamic API responses?
    Compression cost is paid once per artifact for static files and once per request for dynamic ones. Level 11 can be orders of magnitude slower to encode than gzip, so on a hot endpoint it burns CPU and adds directly to time-to-first-byte. Dynamic paths use levels around 4–5, or zstd at a low level.
  • You control both ends of an internal service-to-service call. Which coding and why?
    zstd, usually at a low level. Support is not a question when you own both ends, its decompression is the fastest of the three, and its trained-dictionary support gives a large win on many small, structurally similar JSON payloads. gzip remains a fine fallback if a hop in between is not zstd-aware.
  • Does Brotli's built-in dictionary help equally on all content?
    No. It is a dictionary of common web strings, so it helps most on small HTML, CSS and JS where a general compressor has little history to work with. On large files the window and matching dominate and the dictionary's relative contribution shrinks; on binary or already-compressed data it does nothing useful.

gzip is a suitcase you can pack in a minute; Brotli-11 is vacuum-packing that takes an hour but is the smallest; zstd is a good compression cube — nearly as small, packed in a couple of minutes. You vacuum-pack what you ship once and cube what you pack every day.

saying these in an interview costs you the question

  • Stating flatly that Brotli beats gzip without mentioning compression level or the static/dynamic split.
  • Enabling maximum-level compression on dynamic responses and calling it an optimisation.
  • Dropping gzip support because Brotli exists.
  • Compressing JPEG/PNG/MP4/WOFF2 and expecting savings.
  • Assuming zstd is universally supported by browsers and skipping negotiation.

context