Skip to the Main Content

Note:These pages make extensive use of the latest XHTML and CSS Standards. They ought to look great in any standards-compliant modern browser. Unfortunately, they will probably look horrible in older browsers, like Netscape 4.x and IE 4.x. Moreover, many posts use MathML, which is, currently only supported in Mozilla. My best suggestion (and you will thank me when surfing an ever-increasing number of sites on the web which have been crafted to use the new standards) is to upgrade to the latest version of your browser. If that's not possible, consider moving to the Standards-compliant and open-source Mozilla browser.

February 15, 2019

Brotli

I finally got around to enabling Brotli compression on Golem. Reading the manual, I came across the BrotliAlterETag directive:

Description: How the outgoing ETag header should be modified during compression
Syntax: BrotliAlterETag AddSuffix|NoChange|Remove

with the description:

AddSuffix
Append the compression method onto the end of the ETag, causing compressed and uncompressed representations to have unique ETags. In another dynamic compression module, mod_deflate, this has been the default since 2.4.0. This setting prevents serving “HTTP Not Modified (304)” responses to conditional requests for compressed content.
NoChange
Don’t change the ETag on a compressed response. In another dynamic compression module, mod_deflate, this has been the default prior to 2.4.0. This setting does not satisfy the HTTP/1.1 property that all representations of the same resource have unique ETags.
Remove
Remove the ETag header from compressed responses. This prevents some conditional requests from being possible, but avoids the shortcomings of the preceding options.

Sure enough, it turns out that ETags+compression have been completely broken in Apache 2.4.x. Two methods for saving bandwidth, and delivering pages faster, cancel each other out and chew up more bandwidth than if one or the other were disabled.

To unpack this a little further, the first time your browser requests a page, Apache computes a hash of the page and sends that along as a header in the response

etag: "38f7-56d65f4a2fcc0"

When your browser requests the page again, it sends an

If-None-Match: "38f7-56d65f4a2fcc0"

header in the request. If that matches the hash of the page, Apaches sends a “HTTP Not Modified (304)” response, telling your browser the page is unchanged from the last time it requested it.

If the page is compressed, using mod_deflate, then the header Apache sends is slightly different

etag: "38f7-56d65f4a2fcc0-gzip"

So, when your browser sends its request with an

If-None-Match: "38f7-56d65f4a2fcc0-gzip"

header, Apache compares “38f7-56d65f4a2fcc0-gzip” with the hash of the page, concludes that they don’t match, and sends the whole page again (thus wasting all the bandwidth you originally saved by sending the page compressed).

This is completely brain-dead. And, even though the problem has been around for years, the Apache folks don’t seem to have gotten around to fixing it. Instead, they just replicated the problem in mod_brotli (with a “-br” suffix replacing “-gzip”).

The solution is drop-dead simple. Add the line

RequestHeader edit "If-None-Match" '^"((.*)-(gzip|br))"$' '"$1", "$2"'

to your Apache configuration file. This gives Apache two ETags to compare with: the one with the suffix and the original unmodified one. The latter will match the hash of the file and Apache will return a “HTTP Not Modified (304)” as expected.

Why Apache didn’t just implement this in their code is beyond me.

Posted by distler at February 15, 2019 9:47 AM

TrackBack URL for this Entry:   https://golem.ph.utexas.edu/cgi-bin/MT-3.0/dxy-tb.fcgi/3088

1 Comment & 0 Trackbacks

Re: Brotli

hmm, but now you got the issue that if you send a request with if-none-match: "0-5debc62fd30dd-br" it’ll respond with etag: "0-5debc62fd30dd" (which is missing the -br part)

something like this could solve it though

SetEnvIf If-None-Match '^"((.*)-(gzip))"$' gzip

SetEnvIf If-None-Match '^"((.*)-(br))"$' br

RequestHeader edit "If-None-Match" '^"((.*)-(gzip|br))"$' '"$1", "$2"'

Header edit "ETag" '^"(.*)"$' '"$1-gzip"' env=gzip

Header edit "ETag" '^"(.*)"$' '"$1-br"' env=br

Posted by: FurriousFox on March 28, 2023 11:24 AM | Permalink | Reply to this

Post a New Comment