Compressing the same file every time
Apache compresses responses on the fly. For a page that is different every time, that is the only option. For a stylesheet that changes once a month, it means the server does the same work again for every visitor, for weeks, and throws the result away each time. There is no cache for it.
Measured on 19 August 2026 over an existing connection, with this site's own style.css (124 kB) and world.js (102 kB):
| time | bytes | |
| no compression | 2.9 ms | 124,755 |
| gzip, on the fly | 18.1 ms | 34,995 |
| brotli, on the fly | 14.5 ms | 32,685 |
| world.js, gzip | 47.9 ms | 38,283 |
| world.js, brotli | 15.9 ms | 38,086 |
Forty-eight milliseconds of CPU, per request, to produce a byte-for-byte identical result. On a Raspberry Pi that is not a rounding error.
Compress once, on disk
A build step writes the compressed file next to the original, and a rewrite rule serves it when the browser says it accepts gzip. Serving a finished file costs what serving any file costs.
gzip rather than brotli, for a boring reason: there is no brotli binary installed on this machine. The files come out about 7 % larger than brotli would make them, and the server time is a fifth of what on-the-fly brotli costs. That trade was worth taking now rather than waiting for a nicer answer.
The version goes in the filename
The build writes style.css.<mtime>.gz, and the ?v= in the markup is that same modification time. The rewrite only fires when a file with the matching number exists.
That arrangement has a property worth designing for on purpose: if you change the stylesheet and forget to rebuild, the number in the markup points at a file that does not exist, the rewrite does not fire, and Apache serves the original normally. The worst case is that the speed-up is missing. A stale stylesheet cannot come out. Every caching scheme should be checked against that question — when it goes wrong, does it get slow or does it get wrong?
The bug in the middle of it
The file being served is already gzip, so it must not be compressed again on the way out. The configuration originally set no-gzip, which stops one of Apache's two compressors. The other one, mod_brotli, cheerfully brotli-compressed the finished gzip file while the response header kept saying gzip.
Browsers got unreadable content. It was not caught by looking at the page — it was caught by adding up the bytes and finding a number that made no sense. Both no-gzip and no-brotli are needed, and only one of them is the obvious one.