In an earlier breakdown about site speed, one of the checks was: measure the weight of the page and make sure compression works. The check is right, the test is simple — and it is exactly where we caught ourselves out. Our site passed it. Compression worked. Just not the one that mattered, and not for the people who mattered.
This article is a breakdown of our own case with two figures: 169.7 KB before and 76.2 KB after, with the page content unchanged.
The check that lies
The usual way to confirm compression is enabled looks like this:
curl -s -H "Accept-Encoding: br" -o /dev/null \
-w "%{size_download} bytes\n" https://your-site.com/
A small number comes back and you are satisfied. The problem is the header you have just sent. You asked for brotli only — so of course the server gave you brotli.
A real Chrome asks for something else:
Accept-Encoding: gzip, deflate, br, zstd
That is a list of what the browser can handle, not a demand. The server picks. And on 21 August 2026 our server answered that header with gzip at 169.7 KB, while brotli of the very same page weighed 76 KB. The better option existed, sat right there, and reached no live visitor at all.
One rule follows: measure with the header a browser actually sends. A measurement with Accept-Encoding: br produces a pretty number that nobody receives.
curl -s -H "Accept-Encoding: gzip, deflate, br, zstd" -o /dev/null \
-w "size: %{size_download} bytes\n" \
-D - https://your-site.com/ | grep -i content-encoding
The last line shows what actually arrived: br, gzip, or nothing.
Two causes, both non-obvious
First: the application compressed it first. Many frameworks compress the response themselves, and do so by default, with no configuration at all. The response then reaches the proxy — which sees an already compressed stream and leaves it alone. That is correct behaviour: decompressing and recompressing someone else's work would be waste. The consequence is that your carefully configured brotli on the proxy is simply never called.
In our case the framework only knows gzip. So the chain ran: application compressed to gzip → proxy saw a finished job and passed it through → browser received gzip. Brotli never took part.
Second: that gzip was weaker than the fastest gzip. This is the strangest part of the measurement. We took the same page and compressed it locally:
| Method | Size |
|---|---|
gzip -1 (fastest level) |
143.6 KB |
gzip -9 (strongest) |
111.0 KB |
| what the server served | 169.7 KB |
The server lost to even the weakest level by 18%. The cause is not in the settings but in how a modern page is served: not as a finished document but as a stream, in chunks, as the server assembles it. Streaming compression never sees the whole document and cannot exploit repetition that an archiver would spot on a finished file. That is the price of a faster first byte.
What we did
We switched compression off inside the application entirely. Raw HTML now leaves the container — 788 KB, and that is fine: this stream never leaves the machine.
Compression is done by the proxy, and it is given an explicit order of encodings: brotli first, gzip second. The order has to be set by hand — the default queue in Traefik puts gzip first, which is precisely why it kept choosing the worse option with brotli sitting right there.
The result on the live site:
| Client | Before | After |
|---|---|---|
| Chrome (asks for everything) | 169.7 KB | 76.2 KB |
| client without brotli | 169.7 KB | 120.8 KB |
Minus 55% on every load of the home page, with no change to the content.
A trap that can cost you someone else's site. Container management panels often hand every application the same, shared-by-name compression rule. The namespace on the proxy is shared. That means «I'll just add brotli to the existing rule» changes behaviour not for one site but for everyone using that name — and two different definitions of one name produce non-deterministic results. The rule must be created under its own name and attached to its own route. There is no other safe way.
The other half: the weight is not in the markup
While hunting for the missing brotli we found something else. The question «what actually weighs anything on this page» has an unexpected answer for any modern frontend.
An analysis on 16 August 2026: in the compressed page, markup was 37 KB while serialized data for components was 107 KB. Three quarters of the weight is not what you see in the page source but what the framework ships to the browser to bring interactive blocks to life.
The same run measured something that usually frightens developers: 130 inline SVG icons cost 4 KB after compression. Compression handles repetition beautifully, so «lots of identical icons» is not a problem, however much intuition says otherwise.
And in the data sat something that had no business being there: complete translation objects. On the Ukrainian home page — 18 of them, each with English and Russian branches inside; on the portfolio page — 62. The cause was architectural: blocks applied the translation in the browser, so the server had to ship every language to every visitor.
We moved translation to the server. The result:
| Page | Before | After |
|---|---|---|
| portfolio | 216 KB | 138 KB |
| home | 201 KB | 160 KB |
| English home | 189 KB | 151 KB |
How to look at this on your own site
Three checks, a few minutes each.
1. What the browser actually receives. A Chrome-like header, then look at content-encoding and the size — the command at the top of this article. If gzip arrived while your proxy is configured for brotli, find out who compressed earlier.
2. Whether your server loses to a plain archiver.
curl -s https://your-site.com/ > page.html
gzip -9 -c page.html | wc -c
Compare with what the server served. If the server is heavier, compression is streaming or the level is set too low.
3. How much of the weight is data rather than markup. In DevTools, the Network tab, filter by Doc, look at the document size. Then in the page source look for the large data blocks the framework inserts for hydration. If they outweigh the visible markup several times over, that is what needs optimizing — not the images.
One specific thing worth searching for in that data: whether it carries everything that only some visitors need — all languages, full reference lists, internal database fields. That is the most common and cheapest find.
Honest limits
Minus 90 KB per load is noticeable on mobile internet and almost invisible on a wired connection. Compression does not cure a slow server: if your time to first byte is 800 milliseconds, page weight is not your main problem and you should start from the other end.
Nor does it affect rankings directly. The influence is indirect and honestly described in Google's documentation: speed is part of page experience signals, which work as one factor among many rather than as a lever. We fixed this not for search but because serving a page twice as heavy while having brotli configured is simply waste.
And the main conclusion, which transfers to any site: a check performed in the way that suits the server tells you about the server, not about the visitor. The header you measure with has to match the one the browser sends. Otherwise everything is excellent — right up until the first real user.


