What Content-Encoding says
Content-Encoding names the compression (or other coding) that has been applied to the message body. A response with Content-Encoding: gzip tells the recipient: "these bytes are gzip data; decompress them to get the representation described by Content-Type." The Content-Type still describes the decoded data — a gzip-compressed HTML page is Content-Type: text/html plus Content-Encoding: gzip, not application/gzip.
On responses, the server should only use a coding the client listed in Accept-Encoding. Browsers decode the body transparently, which is why DevTools shows two sizes for every compressed resource.
Accept-Encoding vs Content-Encoding
| Accept-Encoding | Content-Encoding | |
|---|---|---|
| Sent in | Requests | Responses (and request bodies) |
| Meaning | "I can decode these" | "This body is encoded with this" |
| Values | A list, optionally with q-values | The coding(s) actually applied, in order |
| Typical example | gzip, deflate, br, zstd | br |
If you are decoding a specific request string such as gzip, deflate, br, see Accept-Encoding values explained.
Registered values
Content codings are registered with IANA in the HTTP Content Coding Registry. The ones you meet on the web are:
gzip(and the legacy aliasx-gzip) — see gzip.br— Brotli, see Brotli.zstd— Zstandard, see Zstandard.deflate— zlib-wrapped DEFLATE. Historically some servers sent raw DEFLATE under this name, which is why clients learned to accept both and why deflate is best avoided.compress/x-compress— obsolete LZW coding.identity— no coding. It is meaningful inAccept-Encoding; a response should simply omitContent-Encodinginstead of sendingidentity.
The registry also contains specialised codings such as aes128gcm (used by Web Push) and the dictionary-based codings used by Compression Dictionary Transport. Ordinary web servers do not produce these unless you configure them deliberately.
Multiple codings
The header can list more than one coding, in the order they were applied: Content-Encoding: gzip, br means "gzip first, then Brotli on top", so the recipient must undo Brotli, then gzip. Legitimate stacked codings are very rare in practice. When you see one, it is almost always a bug — typically an application that compresses its own output behind a server or proxy that compresses again. Browsers vary in how well they cope, and many API clients do not support stacked codings at all; see decoding errors.
Content-Encoding vs Transfer-Encoding
Transfer-Encoding is a hop-by-hop property of one HTTP/1.1 connection; Content-Encoding is an end-to-end property of the representation. In practice:
Transfer-Encoding: chunkedis how HTTP/1.1 streams a body whose length is not known in advance. It is unrelated to compression, and a gzip response produced on the fly is often also chunked.Transfer-Encoding: gzipexists in the specification but is essentially unsupported by browsers and servers. UseContent-Encodingfor compression.- HTTP/2 and HTTP/3 do not use
Transfer-Encodingat all; framing replaces chunking.Content-Encodingworks identically on every HTTP version.
Content-Length, transfer size and resource size
When a body is compressed, Content-Length is the length of the encoded bytes on the wire, not the decompressed size. A proxy or middleware that decompresses or recompresses a body must recompute or remove Content-Length; forgetting to do so is a classic cause of truncated or hanging responses.
Browser DevTools reflect the same split. In the Network panel, the transferred size is the compressed size plus headers, and the resource (or "size") column is the decoded size. If both numbers are roughly equal for a text resource, the response was not compressed.
ETags, caching and ranges
- ETags. A gzip body and a Brotli body of the same file are different byte sequences. Servers handle this differently: Nginx, for example, downgrades a strong ETag to a weak one (
W/"...") when it compresses on the fly. Do not be surprised by theW/prefix. - Caching. Because the body depends on the request's
Accept-Encoding, compressed responses needVary: Accept-Encodingso shared caches store the variants separately. - Range requests. Byte ranges apply to the encoded representation. Servers commonly disable on-the-fly compression for range requests, or ignore the
Rangeheader for compressed responses, so resumable downloads of large files are usually served uncompressed.
"Content-Encoding: gzip" on a file download
Serving a .gz archive is a different thing from compressing a response. If a user should download backup.tar.gz as a file, send Content-Type: application/gzip and no Content-Encoding. If you add Content-Encoding: gzip, the browser decompresses the body on the fly and saves an uncompressed file under a .gz name. The same confusion affects .svgz files: when served over HTTP they need Content-Type: image/svg+xml plus Content-Encoding: gzip, but opened from the local file system there is no HTTP header to tell the browser to decompress them.
Checking the header
# Ask for gzip and show only the relevant headers
curl -s -o /dev/null -D - -H "Accept-Encoding: gzip" https://example.com/ \
| grep -iE '^(content-encoding|content-length|content-type|vary|etag):'
# Download the raw (still compressed) body and inspect the first bytes
curl -s -H "Accept-Encoding: gzip" https://example.com/ | head -c 4 | xxd
# 1f8b.... = gzip magic number
More approaches are covered on the testing tools page.
Frequently asked questions
What is the difference between Accept-Encoding and Content-Encoding?
Accept-Encoding is a request header listing the codings the client can decode. Content-Encoding is the header on a message that states which coding was actually applied to its body.
Is Content-Length the compressed or uncompressed size?
The compressed size. Content-Length always counts the bytes of the message body as sent, after any content coding has been applied.
What is the difference between Content-Encoding and Transfer-Encoding?
Content-Encoding is an end-to-end property of the representation (such as gzip compression). Transfer-Encoding is a hop-by-hop HTTP/1.1 mechanism, in practice almost always chunked, and is not used in HTTP/2 or HTTP/3.
Is Content-Encoding: deflate dangerous?
Not dangerous, but unreliable. The name has historically been used for both zlib-wrapped and raw DEFLATE data, and some clients mishandle one or the other. Prefer gzip, Brotli or Zstandard.
Should a response send Content-Encoding: identity?
No. identity is meaningful in Accept-Encoding; an uncompressed response should simply omit the Content-Encoding header.