Back to HTTP Headers

Accept-Encoding content request

Lists the compression algorithms the client supports — so the server knows which encoding to use on the response body.

What it does

Accept-Encoding tells the server which compression formats the client can decompress. The server picks the best supported encoding, compresses the response body, and declares its choice in Content-Encoding. This transparent compression typically cuts response sizes by 60–80% for text content.

Syntax

Accept-Encoding: gzip
Accept-Encoding: gzip, br
Accept-Encoding: gzip, deflate, br, zstd
Accept-Encoding: *
Accept-Encoding: identity
Accept-Encoding: gzip;q=1.0, deflate;q=0.5, *;q=0

Encoding values

Value Algorithm Notes
gzip GNU zip (LZ77 + CRC32) Universal support, always safe to request
br Brotli Better compression than gzip, all modern browsers, HTTPS-only in browsers
zstd Zstandard Newer, excellent ratios, growing adoption
deflate zlib/deflate Avoid — ambiguous implementation, inconsistent across servers
identity No encoding Explicitly request uncompressed
* Any encoding Accept whatever the server prefers

Quality values

Q-factors express preference, same as Accept:

Accept-Encoding: br;q=1.0, gzip;q=0.8, *;q=0.1

Prefer brotli, fall back to gzip, accept anything else as last resort.

Most clients just list supported encodings without q-factors and let the server choose optimally:

Accept-Encoding: gzip, deflate, br, zstd

What browsers send

A modern Chrome Accept-Encoding:

Accept-Encoding: gzip, deflate, br, zstd

The browser supports all four. The server typically picks br (best compression) or gzip (best compatibility) based on its configuration.

The Vary: Accept-Encoding requirement

This is the most important gotcha with Accept-Encoding. When a server compresses responses, it must include Vary: Accept-Encoding so caches store separate versions for clients with different encoding support:

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

Without Vary: Accept-Encoding, a CDN might cache the brotli-compressed version and serve it to a client that sent Accept-Encoding: gzip only — the client receives brotli bytes it can't decode. Garbled content, no error message, very confusing to debug.

Most web servers (nginx, Apache, Caddy) set Vary: Accept-Encoding automatically when they compress. Application-level compression middleware usually does too. But if you're setting Content-Encoding manually, you're responsible.

Disabling compression

To explicitly request no compression:

Accept-Encoding: identity

Or exclude specific encodings:

Accept-Encoding: gzip;q=0, br;q=0, *;q=0

(q=0 means "not acceptable")

This is useful when debugging responses and you want to read the raw body, or when piping HTTP output to tools that can't decompress on the fly.

# curl — disable compression
curl --compressed-off https://example.com/api/data
# or
curl -H "Accept-Encoding: identity" https://example.com/api/data

How it interacts with Content-Encoding

These two headers form a negotiation pair:

  • Client: Accept-Encoding: gzip, br ("I can handle these")
  • Server: Content-Encoding: br ("I compressed it with this")

The server should only use an encoding the client listed. If the client didn't list zstd and the server sends Content-Encoding: zstd anyway, the client can't decompress it.

Common mistakes and gotchas

Compressing already-compressed content. Applying gzip to JPEG images, ZIP archives, or MP4 videos adds CPU overhead and usually makes the response slightly larger. Most web servers skip compression for known binary formats. Application-level compression middleware may not — check your config.

Double compression. Application compresses with gzip, then nginx compresses again. The result: a file compressed twice, usually larger than once, and potentially requiring two decompressions. Ensure compression happens at exactly one layer.

Not sending Accept-Encoding in API clients. HTTP libraries usually send Accept-Encoding: gzip, deflate by default. Raw HTTP clients built from scratch might not. If your API client isn't receiving compressed responses, check whether it's sending Accept-Encoding.

Real-world examples

Browser request (all modern encodings):

GET /large-page HTTP/1.1
Host: example.com
Accept-Encoding: gzip, deflate, br, zstd

Server responding with brotli:

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

curl with explicit encoding request:

curl -H "Accept-Encoding: br" -I https://example.com

FAQ

Why don't browsers use brotli over HTTP (only HTTPS)?

When brotli was introduced, Chrome's team found that some HTTP middleboxes (transparent proxies, network appliances) would corrupt content-encoded responses they didn't recognise. Since HTTPS traffic is opaque to these boxes, brotli over HTTPS is safe. Over HTTP, broken middleboxes could corrupt brotli responses silently. The HTTPS-only restriction was a pragmatic safety measure.

Does Accept-Encoding affect JSON API responses?

Yes — JSON compresses extremely well (often 80%+ reduction). Large API responses should be compressed. Most API clients (Python requests, axios, fetch) send Accept-Encoding: gzip, deflate by default and decompress transparently.

What happens if the server doesn't support any listed encoding?

The server should return the response uncompressed with no Content-Encoding header. It should not return 406 Not Acceptable — unlike Accept, an unsupported encoding doesn't warrant rejection of the whole request.

Fun fact

The web's adoption of brotli over gzip for static assets represents one of the most successful compression transitions in internet history. Google open-sourced brotli in 2015, and within 5 years it became the default choice for pre-compressed static assets on most major CDNs. A typical HTML/CSS/JS bundle compressed with brotli at level 11 is 15–25% smaller than the same bundle at gzip's best level — a meaningful bandwidth saving at web scale. The Accept-Encoding: br capability flag in billions of browser requests per second is what made this transition happen seamlessly and automatically.