gzip, Brotli and zstd: HTTP Compression Measured

Management Summary
HTTP compression packs website content more efficiently for transfer, reducing the amount of data sent to a browser. gzip, Brotli and zstd are different methods for doing this. A sample of twenty domains illustrates actual use, but cannot rank the methods because the sites serve different content. The useful business balance is transfer size against computing effort and loading performance. Smaller transfers help, yet do not make a complete page proportionally faster because other parts of loading still take time.
Contents
HTTP compression reduces the bytes needed to transfer a response body. The result depends on the content, compression settings and the response actually returned. A successful HTTP status alone does not prove that the requested homepage arrived.
A check of twenty domains on 24 October 2026 produced seventeen regular HTML responses and three access-block responses. Of the seventeen regular responses, eleven used gzip, five used Brotli and one had no Content-Encoding. No response selected zstd. These are observations from one request configuration, not market shares or permanent website settings.

How the response encoding is selected
Accept-Encoding advertises acceptable response encodings. The sample offered gzip, deflate, br, zstd with equal default weights. Server capabilities, configuration and resource type influence the selection; the order of this list is not a guaranteed preference order. An unencoded response is normally acceptable unless explicitly excluded.
Content-Encoding identifies the encoding actually applied. Absence of the field means no content coding is declared, not that the body must match a file on disk. HTML can be generated dynamically. Encodings can also be stacked; decoding then reverses the listed application order.
When the representation varies with this request field, Vary: Accept-Encoding helps compliant caches keep variants separate. The header is not proof that every intermediary is configured correctly. Cached or precompressed responses can be selected without recompressing the content for every request.
gzip, Brotli, zstd and deflate
- gzip: widely supported, but not guaranteed in every client or tool build. The advertised capabilities still matter.
- Brotli (
br): often effective for text. Quality settings trade encoding effort against output size; no fixed speed or size advantage applies to every input. - Zstandard (
zstd): designed for a useful speed-to-compression trade-off. Results depend on level, implementation, hardware and data. Zero selections in this sample do not imply missing browser support or universal server non-support. - deflate: the HTTP coding uses a zlib wrapper around DEFLATE. gzip also commonly uses DEFLATE, with a different wrapper. The formats are not interchangeable.
Actual browser offers depend on browser version and context. A manually constructed curl request is not a browser compatibility test.
The measured responses
The measurement ran from a Raspberry Pi between 04:45:33 and 04:45:37 UTC on 24 October 2026. curl 8.14.1 supported all four offered decoders. Each domain received one retrieval chain, with HTTPS redirects followed, no login or browser JavaScript, and the default curl user agent. The table describes the final response. Headers, decoded bodies and curl metadata were retained for verification.
| Domain | HTTP | Content-Encoding | Encoded bytes | Decoded bytes | Classification |
|---|---|---|---|---|---|
| wikipedia.org | 200 | gzip | 28883 | 122421 | regular HTML |
| github.com | 200 | gzip | 118918 | 580257 | regular HTML |
| bbc.com | 200 | gzip | 92972 | 649846 | regular HTML |
| nytimes.com | 403 | absent | 771 | 771 | access block |
| cloudflare.com | 200 | br | 109639 | 1333885 | regular HTML |
| mozilla.org | 200 | br | 8032 | 47807 | regular HTML |
| apple.com | 200 | gzip | 47325 | 326751 | regular HTML |
| microsoft.com | 200 | absent | 201253 | 201253 | access block |
| spiegel.de | 200 | gzip | 272825 | 1958177 | regular HTML |
| heise.de | 200 | gzip | 135977 | 936097 | regular HTML |
| python.org | 200 | absent | 52766 | 52766 | regular HTML |
| debian.org | 200 | gzip | 4279 | 15864 | regular HTML |
| kernel.org | 200 | gzip | 3438 | 19152 | regular HTML |
| gnu.org | 200 | gzip | 9806 | 31463 | regular HTML |
| w3.org | 200 | br | 8996 | 49520 | regular HTML |
| nginx.org | 200 | gzip | 2206 | 6461 | regular HTML |
| postgresql.org | 200 | gzip | 5914 | 25040 | regular HTML |
| zeit.de | 403 | absent | 5439 | 5439 | access block |
| tagesschau.de | 200 | br | 128481 | 1613994 | regular HTML |
| php.net | 200 | br | 11789 | 60308 | regular HTML |
New York Times and ZEIT returned HTTP 403. Microsoft returned HTTP 200 but an explicit access-block page. Those three responses are excluded from the regular-homepage totals. The Microsoft body contained 201,253 bytes with no declared content coding; this does not establish the compression policy of the actual Microsoft homepage.
Python.org returned regular homepage HTML without Content-Encoding in this run. A no-JavaScript notice occurs inside that homepage; the notice alone is not a separate access-block page. Earlier or differently configured requests can receive another representation.

Ratio and saving are different numbers
The ratio is decoded body bytes divided by encoded body bytes. A ratio of 4 means the encoded body is one quarter of the decoded size: a 75% reduction. It does not mean a fourfold improvement in page-load speed.
Among the compressed regular responses, the ratios ranged from 2.93 at nginx.org to 12.56 at tagesschau.de. For tagesschau.de, 1,613,994 decoded bytes corresponded to 128,481 encoded bytes, about 92.0% fewer body bytes. These numbers exclude response headers, TLS and transport framing, other page resources and rendering work.
Different websites provide different inputs, so these ratios cannot rank the algorithms. A fair algorithm comparison compresses identical bytes at documented settings and measures CPU time as well as size. Repetition, not simply file length or use of a framework, determines how much redundancy is available.
A measurement needs matching bodies
Comparing two separate requests can mix different content, redirects, error pages or cache variants. An identity request also needs its actual Content-Encoding checked. Comparing the transferred and decoded sizes of the same response avoids mixing different content, but still requires a complete successful transfer, correct decoding and content inspection.
In this measurement, curl used --compressed: the saved body was decoded, while size_download recorded transferred body bytes. Saved response headers still describe the encoding on the response. The saved file must therefore not be decoded again merely because those headers contain gzip.
Questions and answers
Is extra compression useful for images and fonts?
Already compressed JPEG, PNG, WebP, AVIF and WOFF2 often gain little and can grow. SVG is text and commonly benefits. File extensions alone are insufficient; actual bytes and MIME type matter.
Should Brotli always run at its highest quality?
No. Brotli supports quality levels 0–11. Higher settings can spend substantially more encoding time. Static assets can be precompressed during a build; for dynamic responses, the compression effort must fit the available processor capacity and acceptable response time. gzip and zstd can also be precompressed. No universal best level follows from this sample.
Does HTTPS make compression harmless?
No. With BREACH-style attacks, changes in compressed response length can reveal information about secrets. This risk can arise when secrets and attacker-influenced content share a compressed response and repeated response lengths can be observed. Ordinary HTML escaping alone does not remove that condition. Depending on the application, mitigations include disabling compression on affected responses, separating secrets from reflected input or correctly masking tokens. TLS 1.3 removes TLS-level compression, but does not remove HTTP Content-Encoding or its side-channel risks.
How can one gzip response be checked manually?
The following curl command is intended for a new empty working directory and a small trusted HTTPS resource. It disables curlrc loading, does not automatically decode and does not follow redirects:
curl -q --fail --silent --show-error --max-time 30 --max-filesize 10000000 --proto "=https" -H "Accept-Encoding: gzip" -D headers.txt -o body.bin -w "%{http_code}\n" https://example.com/
Before decoding, the printed status must be 200 and the final headers must declare exactly Content-Encoding: gzip. The decoded content must subsequently be checked against the expected resource before interpreting the sizes. A redirect, block page, missing encoding or failed curl exit is not a gzip measurement. After those checks:
gzip -t body.bin &&
gzip -dc body.bin > decoded.html &&
wc -c body.bin decoded.html
The first command checks gzip integrity; subsequent commands run only on success. The two byte counts belong to the same response. Decompression can greatly expand data, so available disk space and trusted input matter. A missing Content-Encoding is an unencoded-response finding, not a reason to force gzip decoding. Other encodings require the matching decoder.