HTTP Compression

HTTP compression reduces the size of data transferred between servers and clients. A client sends an Accept-Encoding header listing supported content codings. The server picks one, compresses the response body, and indicates the choice in a Content-Encoding header. This exchange is a form of proactive content negotiation.

Baseline

Brotli compression is widely available across all major browsers. Zstandard is newly available. Compression Dictionary Transport is newly available in Chromium-based browsers.

How content coding works

HTTP compression operates at the representation level. The server applies a content coding to the response body before transmission. The client reverses the coding after receiving the full response.

The Accept-Encoding request header lists acceptable codings with optional quality values:

Accept-Encoding: br, gzip;q=0.8, zstd;q=0.9

The server selects one coding and returns the compressed body with two key headers:

Vary header and caching

Servers returning different encodings based on the request must include Vary: Accept-Encoding in the response. Without this header, a cache stores one encoded variant and serves the cached variant to clients expecting a different encoding or no encoding at all.

Content codings

The content coding framework defines registered codings. The IANA Content Coding Registry tracks all registered codings. The most common ones used in HTTP:

gzip

The gzip coding uses the GZIP file format , combining LZ77 and Huffman coding. Supported by every HTTP client and server. The gzip coding remains the most widely deployed content coding on the web.

br (Brotli)

The br coding uses the Brotli algorithm , developed by Google. Brotli achieves higher compression ratios than gzip at comparable decompression speeds. Brotli includes a built-in static dictionary of common web content patterns, an advantage for compressing HTML, CSS, and JavaScript.

All major browsers support Brotli over HTTPS connections.

zstd (Zstandard)

The zstd coding uses the Zstandard algorithm , developed at Facebook. Zstandard offers a wide range of compression levels, from fast modes exceeding gzip speed to high modes rivaling Brotli compression ratios. Zstandard decompression is fast regardless of the compression level used.

When used as an HTTP content coding, Zstandard encoders must limit the window size to 8 MB and decoders must support at least 8 MB. This cap prevents excessive memory consumption in browsers and other HTTP clients.

dcb (Dictionary-Compressed Brotli)

The dcb coding compresses a response using Brotli with an external dictionary. The compressed stream includes a fixed header containing the SHA-256 hash of the dictionary, allowing the client to verify the dictionary matches before decompression. Dictionary-based Brotli supports compression windows up to 16 MB.

The dcb coding is part of the Compression Dictionary Transport framework. See dictionary compression below.

dcz (Dictionary-Compressed Zstandard)

The dcz coding compresses a response using Zstandard with an external dictionary. Like dcb, the compressed stream starts with a header containing the SHA-256 hash of the dictionary. The header is structured as a Zstandard skippable frame, making the format compatible with existing Zstandard decoders.

The dcz coding is part of the Compression Dictionary Transport framework. See dictionary compression below.

deflate

The deflate coding wraps a DEFLATE compressed stream inside the zlib format . Historical inconsistencies between implementations (some sent raw DEFLATE without the zlib wrapper) made deflate unreliable. Modern HTTP favors gzip or br instead.

compress

The compress coding uses adaptive Lempel-Ziv- Welch (LZW). Rarely encountered in modern HTTP.

identity

The identity coding means no transformation was applied. This value appears in Accept-Encoding to signal acceptance of uncompressed responses. A server is always allowed to send an uncompressed response, even when the client lists only compression codings.

Lossy vs. lossless compression

HTTP content coding is exclusively lossless. Every content coding registered for use with Content-Encoding (gzip, br, zstd, deflate) reconstructs the original bytes exactly on decompression.

Lossy compression exists in media formats like JPEG, MP3, and H.264. These formats are applied at the content creation stage, before HTTP transmission. A JPEG image served over HTTP is already lossy at the format level. Applying HTTP content coding (gzip or br) to a JPEG adds lossless compression on top, often with negligible size reduction, since the JPEG data is already compressed.

Skip compression for pre-compressed formats

Applying content coding to pre-compressed media (JPEG, PNG, MP4, WOFF2) wastes CPU cycles for minimal size savings. Server configurations often exclude these MIME types from compression.

End-to-end compression

End-to-end Compression compresses data at the origin and decompresses at the final client. Intermediaries such as proxies, load balancers, and CDNs pass the compressed body through without modification.

The negotiation flow:

  1. Client sends Accept-Encoding listing supported codings
  2. Server selects a coding and compresses the response body
  3. Server returns Content-Encoding and Vary: Accept-Encoding
  4. Client decompresses the body

This is the standard compression model in HTTP and the one most servers implement.

Hop-by-hop compression

Hop-by-hop compression uses Transfer-Encoding instead of Content-Encoding. The compression applies between individual hops (client to proxy, proxy to proxy, proxy to server) rather than across the entire path.

The client sends a TE header to indicate which transfer codings are acceptable. The receiving server or intermediary selects a coding and applies the compression for one hop. The next hop negotiates independently.

HTTP/2 restriction on TE

HTTP/2 restricts the TE header to the value trailers only. Hop-by-hop transfer coding compression is not available in HTTP/2 or HTTP/3.

Limited server support

Hop-by-hop compression is rarely implemented. Most servers and proxies use end-to-end content coding instead.

Dictionary compression

Standard content codings like gzip, br, and zstd compress each response independently. Dictionary compression takes a different approach: a previously fetched resource serves as a shared dictionary, giving the compressor a head start on patterns already known to both sides. The result is significantly smaller deltas, especially for resources with incremental changes between versions (updated JavaScript bundles, CSS files, or API responses).

The Compression Dictionary Transport framework defines how clients and servers negotiate dictionary use over HTTPS.

How dictionary negotiation works

The negotiation spans multiple requests:

  1. A client fetches a resource (or a dedicated dictionary file). The server responds with a Use-As-Dictionary header specifying a URL pattern for future requests where the resource applies as a dictionary
  2. The client stores the response content alongside its SHA-256 hash and the matching rules
  3. On a later request matching the URL pattern, the client sends an Available-Dictionary header containing the dictionary hash. If the server assigned an identifier to the dictionary, the client also sends a Dictionary-ID header
  4. The server compresses the response against the dictionary and returns the body with Content-Encoding: dcb or Content-Encoding: dcz
  5. The client verifies the dictionary hash embedded in the compressed stream, then decompresses using its stored copy of the dictionary

Servers must include Vary: Accept-Encoding, Available-Dictionary on dictionary-compressed responses so caches distinguish between dictionary and non-dictionary variants.

Use-As-Dictionary directives

The Use-As-Dictionary response header is a structured field dictionary. Key directives:

  • match (required) -- a URL pattern string defining which future request paths the dictionary applies to. Uses WHATWG URLPattern syntax, limited to same-origin URLs
  • match-dest -- a list of Fetch request destinations (e.g., "document", "script"). An empty list matches all destinations
  • id -- an opaque server-assigned identifier sent back by the client in the Dictionary-ID header
  • type -- the dictionary format. Defaults to "raw" for an unformatted byte sequence

A server advertising a dedicated dictionary file (rather than reusing an existing resource as a dictionary) sends a Link header with the compression-dictionary relation type:

Link: </dict/app-v2.dat>; rel="compression-dictionary"

This signals the client to fetch the linked resource. The fetched response still needs a Use-As-Dictionary header to define matching rules and activate dictionary storage.

HTTPS only

Dictionary compression requires a secure context. Both the dictionary and the dictionary-compressed response must be served over HTTPS. This prevents intermediaries from misprocessing dictionary-compressed content and protects against compression-based side-channel attacks.

Same-origin restriction

Dictionaries apply to same-origin URLs only. The match pattern in Use-As-Dictionary is restricted to the same origin as the dictionary itself. Cross-origin use is blocked unless the response includes appropriate CORS headers and the dictionary is CORS-readable.

Example

A client requests an HTML page and lists four accepted codings in order of preference. The server selects gzip and returns the compressed response with the appropriate headers.

Request

GET /news.html HTTP/1.1
Host: www.example.re
Accept-Encoding: br, zstd, gzip, deflate

Response

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 5028
Vary: Accept-Encoding

<compressed body>

The Content-Length value (5028) represents the compressed size. The original uncompressed HTML is larger. The Vary: Accept-Encoding header tells caches to store separate variants for different encoding preferences.

A Brotli-compressed response from the same server to a client supporting br:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Encoding: br
Content-Length: 4211
Vary: Accept-Encoding

<compressed body>

Dictionary compression example

A client previously fetched /app.v1.js and the server marked the response as a dictionary for future requests matching /app*.js. On the next visit, the client requests the updated script and offers the stored dictionary.

Request

GET /app.v2.js HTTP/1.1
Host: www.example.re
Accept-Encoding: br, dcb, dcz, zstd, gzip
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:

Response

HTTP/1.1 200 OK
Content-Type: application/javascript
Content-Encoding: dcb
Content-Length: 1483
Vary: Accept-Encoding, Available-Dictionary

<dictionary-compressed body>

The server compressed the response using dictionary-compressed Brotli (dcb). The Content-Length of 1483 bytes is far smaller than a standard Brotli response, since most of the file content already exists in the dictionary. The Vary header includes Available-Dictionary so caches keep dictionary-compressed and regular variants separate.

SEO and compression

Search engine crawlers like Googlebot support gzip and Brotli. Compressed responses reduce crawl bandwidth consumption, allowing more pages to be fetched within a crawl budget. Bingbot sends Accept-Encoding: gzip, deflate.

Takeaway

HTTP compression reduces transfer sizes through lossless content codings negotiated between client and server via Accept-Encoding and Content-Encoding. Brotli and Zstandard offer better ratios than gzip, while gzip remains universally supported. Dictionary compression (dcb and dcz) goes further by using previously fetched resources as shared context, producing smaller deltas for incrementally updated content. Servers include Vary: Accept-Encoding to ensure caches serve the correct variant.

See also

Last updated: March 11, 2026