I work on OnePassApps, so this is our own site's own HTTP behaviour, measured today with plain HTTPS requests from one server - no browser session, no cookies, no JavaScript - on 10 November 2026.
We have already written about which content codings this site can produce: https://dev.to/onepassapps/the-compression-layer-of-apponepassappscom-gzip-brotli-zstd-and-the-73-of-bytes-we-save-4mna. This post is about the negotiation itself: what the server does when the Accept-Encoding header changes.
What I measured
- The 56 URLs in our sitemap.xml
- 21 different Accept-Encoding values, each one sent to all 56 URLs
- One plain GET per request, 1,176 requests in total
- The Content-Encoding, Vary, Content-Length and Transfer-Encoding headers read off every answer
Every one of the 1,176 answers was HTTP 200. Nothing was refused.
The table
| Accept-Encoding sent | Content-Encoding | Vary | bytes over the 56 pages |
|---|---|---|---|
gzip |
gzip | accept-encoding | 357,808 |
br |
br | accept-encoding | 351,179 |
gzip, deflate, br, zstd |
br | accept-encoding | 351,109 |
gzip;q=0.5 |
gzip | accept-encoding | 357,881 |
gzip;q=0.9, br;q=0.1 |
br | accept-encoding | 351,145 |
br;q=0, gzip;q=1 |
gzip | accept-encoding | 357,843 |
identity;q=0 |
(none) | (none) | 1,361,049 |
gzip;q=0 |
(none) | (none) | 1,361,049 |
gzip;q=0, br;q=0, identity;q=0 |
(none) | (none) | 1,361,049 |
* |
(none) | (none) | 1,361,049 |
*;q=0 |
(none) | (none) | 1,361,049 |
deflate |
(none) | (none) | 1,361,049 |
zstd |
(none) | (none) | 1,361,049 |
Four of those twenty-one values produced a compressed body. The other seventeen came back uncompressed, with the same three headers every time.
The wildcard is accepted and then ignored
Accept-Encoding: * is the wildcard. RFC 9110 says the asterisk matches any available content coding the field does not list explicitly, and our server accepts it - it answers 200 like it does everything else.
But it does not use it. On all 56 pages the wildcard produced no Content-Encoding at all, no Vary, and the full 1,361,049 bytes. The same is true of *;q=0. A wildcard asks for anything the server has; our edge hands back the uncompressed body anyway.
A quality value of zero is honoured
Accept-Encoding: gzip;q=0 says gzip is not acceptable. That is respected: on all 56 pages the answer came back with no Content-Encoding and the full 1,361,049 bytes.
The rule that covers the rest is in RFC 9110, section 12.5.3:
g with the highest non-zero qvalue is preferred. An Accept-Encoding header field with a field value that is empty implies that the user agent does not want any content coding in response. If a non-empty Accept-Encoding header field is present in a request and none of the available representations for the response have a content coding that is listed as acceptable, the origin server SHOULD send a response without any content coding unless the identity coding is indicated as unacceptable. When the Accept-Encoding header field is present in a response, it indicates what content codings the resource was willing to accept in the associated request. The field value is evaluated the same way as in a request. Note that this information is specific to the associated request; the
That sentence has an exception clause for the case where identity itself is indicated as unacceptable. We never saw the exception taken: Accept-Encoding: gzip;q=0, br;q=0, identity;q=0 excludes every coding including identity itself, and the answer was HTTP 200 with the uncompressed body on all 56 pages.
The same held for identity;q=0 on its own, and for Accept-Encoding: foo, where foo is not a coding at all: 56 answers, 200, no Content-Encoding.
Which coding wins is decided by br, not by the weights
This is the part that surprised me, so I checked it from four directions.
-
gzip;q=0.5, br;q=0.9- br wins, on all 56 pages -
br;q=0.9, gzip;q=0.5- br again -
gzip;q=0.9, br;q=0.1- br, even with gzip carrying the higher weight -
br, gzip- br
br is picked whenever it is listed with a weight above zero, regardless of the weights themselves. gzip is used only when br is absent or carries a zero: br;q=0, gzip;q=1 gave gzip on all 56 pages, and gzip;q=0.5 alone gave gzip.
deflate and zstd are never chosen, on their own or in a list. Our app host has never served either, which matches what we measured on the compression layer of this site.
Vary appears on exactly the answers that vary
The Vary header tells a cache which request headers change the answer. On this site it is present on the compressed answers and absent on the uncompressed ones:
- gzip, br and the br-winner variants:
Vary: accept-encodingon all 56 answers - identity, the wildcard, gzip;q=0, deflate, zstd and the unknown coding: no Vary header at all on all 56 answers
That refines what we wrote about the compression layer of this site. Vary is there on the HTML pages, and it is there because of the compressed answers - the uncompressed answer to the very same page carries no Vary at all.
The framing is different on the two protocols
HTTP/1.1 and HTTP/2 do not describe the same body the same way, and on this site the difference is complete.
Over HTTP/1.1, all 56 answers carry Transfer-Encoding: chunked and no Content-Length, with Connection: keep-alive.
Over HTTP/2, all 56 answers carry neither a Transfer-Encoding nor a Content-Length. HTTP/2 frames the body itself, so it needs neither header - but it also means there is no length in the headers to read.
Both protocols advertise a third one: alt-svc: h3=":443"; ma=86400 on all 56 answers, over HTTP/1.1 and over HTTP/2 alike.
The bytes
Over the 56 pages, uncompressed, the total is 1,361,049 bytes. That exact total came back the same on four separate passes of this measurement, because the length of the uncompressed body is stable even though its bytes are not - the per-request key we wrote about here: https://dev.to/onepassapps/the-same-url-never-returns-the-same-bytes-56-pages-of-apponepassappscom-and-one-per-request-key-j36.
The compressed totals move by a few tens of bytes between runs for the same reason, so treat them as a snapshot rather than a constant. In this run: gzip 357,808 bytes, br 351,179 bytes.
The largest page is /terms at 74,495 bytes uncompressed, 18,476 under gzip and 18,111 under br. The smallest is /voting at 15,866 bytes, 4,576 under gzip and 4,530 under br.
What I did not measure
One server, in one region, on one day. There was no shared cache in front of me, so I cannot say what a CDN would have stored. The API host behind the site is a different service and a different negotiation - it does serve zstd, which this host does not. I had no HTTP/3 client here, so alt-svc is an advertisement I read rather than a protocol I exercised. I did not vary the User-Agent, did not test Range requests or HEAD, and did not look at what any browser actually sends.
Our home page: https://app.onepassapps.com/?utm_source=devto&utm_medium=community&utm_campaign=accept-encoding-contract&utm_content=home-link
Our app guides: https://app.onepassapps.com/guides?utm_source=devto&utm_medium=community&utm_campaign=accept-encoding-contract&utm_content=guides-link
Top comments (0)