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:
- Content-Encoding names the coding applied
- Content-Length reflects the compressed size, not the original
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:
- Client sends Accept-Encoding listing supported codings
- Server selects a coding and compresses the response body
- Server returns
Content-Encoding and
Vary: Accept-Encoding - 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:
- 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
- The client stores the response content alongside its SHA-256 hash and the matching rules
- 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
- The server compresses the response against the
dictionary and returns the body with
Content-Encoding: dcborContent-Encoding: dcz - 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
The compression-dictionary link relation
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
- RFC 9110: HTTP Semantics, Section 8.4 (Content Codings)
- RFC 7932: Brotli Compressed Data Format
- RFC 8878: Zstandard Compression
- RFC 9659: Window Sizing for Zstandard Content Encoding
- RFC 9841: Shared Brotli Compressed Data Format
- RFC 9842: Compression Dictionary Transport
- Accept-Encoding
- Content-Encoding
- Transfer-Encoding
- Use-As-Dictionary
- Available-Dictionary
- Dictionary-ID
- TE
- Vary
- Content negotiation
- HTTP/2
- HTTP headers