# Accept-Encoding Header

> Learn how Accept-Encoding tells servers which compression formats (gzip, br, deflate) your client supports to reduce bandwidth and speed up page loads.

Source: https://howhttpworks.com/headers/accept-encoding
Last reviewed: 2026-10-05

> **TL;DR:** `Accept-Encoding` is the request header where a client lists the compression formats it can decode, such as `gzip` or `br` (Brotli). The server picks one, or none, and says which in `Content-Encoding`. Browsers set it for you and JavaScript can't change it, so test with curl.

## What is Accept-Encoding?

In a request, this field negotiates content codings. It's separate from [Transfer-Encoding](https://howhttpworks.com/headers/transfer-encoding), which frames an HTTP/1.1 message. Servers can send `Accept-Encoding` too, to advertise which codings they accept in request bodies, for example in a `415` response. [MDN describes both uses](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding).

## How Accept-Encoding Works

Here's a request and response with bodies omitted. Headers and outputs on this page are trimmed examples.

```http
GET /report HTTP/1.1
Host: example.com
Accept-Encoding: gzip, br;q=0.8
```

```http
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: gzip
Vary: Accept-Encoding
```

No `Content-Encoding` in the response means the body wasn't compressed. That's allowed: offering gzip doesn't force the server to use it.

## Common Compression Algorithms

`gzip` is gzip and `br` is Brotli. HTTP's `deflate` is the odd one: it means a zlib wrapper around a DEFLATE stream, not raw DEFLATE. Only advertise formats your client can decode. For curl, `curl --version` lists what your build supports.

## Quality Values and Preferences

```http
Accept-Encoding: br;q=1, gzip;q=0.8
```

This says "Brotli preferred, gzip fine". Plain `br, gzip` gives both the default quality of `1`, because list order isn't a ranking.

## Real-World Examples

Let curl advertise what it supports and decode the response for you:

```bash
curl --compressed -D - -o /dev/null https://example.com/
```

[`--compressed`](https://curl.se/docs/manpage.html#--compressed) decodes the body, but the printed headers still describe the encoded response. So the saved file's size won't match `Content-Length`, which counts compressed bytes.

## Compression Effectiveness

How much you save depends on the content. There's no fixed ratio for HTML, JSON or JavaScript. Measure the same resource both ways and check which coding the server actually chose before crediting compression with the difference.

Responses that arrive already encoded are a good diagnostic case. If the application sends `Content-Encoding: gzip`, look at the raw bytes saved without `--compressed` (the commands in the measuring section below save them to `/tmp/gzip.body`):

```bash
file /tmp/gzip.body
gzip -t /tmp/gzip.body
```

Run the gzip check only after the saved headers confirm the coding. A `200` status says nothing about whether the body is valid gzip, and a gzip failure on a body that was never encoded tells you nothing about negotiation.

## Getting Accept-Encoding right

Inside an nginx `http`, `server`, or `location` block:

```nginx
gzip on;
gzip_types text/plain application/json text/css application/javascript;
gzip_vary on;
```

The [nginx gzip module](https://nginx.org/en/docs/http/ngx_http_gzip_module.html) always compresses `text/html` on top of whatever `gzip_types` lists. Both `gzip` and `gzip_vary` default to `off`, so you need to turn them on. This config gives you gzip only, not Brotli.

In browsers, `Accept-Encoding` is a [forbidden request header](https://fetch.spec.whatwg.org/#forbidden-request-header). Leave it and `Content-Length` out of `fetch()` calls; the browser manages both.

For Caddy serving files from `/srv/www`, turn on its encoders directly:

```caddyfile
example.com {
    root * /srv/www
    encode zstd gzip
    file_server
}
```

[Caddy's `encode` directive](https://caddyserver.com/docs/caddyfile/directives/encode) negotiates these formats from the request. Putting `zstd` first makes it the server's pick when the client doesn't express a stronger preference. A client that only offers gzip gets gzip.

## Measure the savings on your own responses

Swap in the URL you're investigating. These requests save the raw bodies, with no automatic decompression from curl:

```bash
curl -sS -D /tmp/identity.headers -o /tmp/identity.body \
  -H 'Accept-Encoding: identity' https://example.com/
curl -sS -D /tmp/gzip.headers -o /tmp/gzip.body \
  -H 'Accept-Encoding: gzip' https://example.com/
cat /tmp/identity.headers /tmp/gzip.headers
wc -c /tmp/identity.body /tmp/gzip.body
```

Compare `Content-Encoding`, status and validators like `ETag` across both responses. Matching byte counts alone don't prove the server ignored the header.

## Compression Priority and Quality Values

Under [RFC 9110 §12.5.3](https://www.rfc-editor.org/rfc/rfc9110#section-12.5.3), a request with no `Accept-Encoding` field accepts any coding, while an empty field asks for no coding at all. `identity` means "no encoding". It's acceptable by default unless you exclude it with `identity;q=0`, or with `*;q=0` when there's no more specific entry for identity.

`Accept-Encoding: identity` asks for an uncompressed response. Because gzip and Brotli aren't listed, they aren't acceptable, and a server that sends gzip anyway is ignoring the request. Test this case when an intermediary seems to compress every response.

## Related Headers

- [Content-Encoding](https://howhttpworks.com/headers/content-encoding)
- [Vary](https://howhttpworks.com/headers/vary)
- [Content-Length](https://howhttpworks.com/headers/content-length)
