Apache Web 服务器对 ETag 和 Deflate 的处理
ETag
- HTTP请求头中如果包含
If-None-Match: "44d0ac3fd1f00"
- 服务器如果发现内容没有发生变化,可以返回
HTTP/1.1 304 Not Modified
这样做的主要好处是可以节省网络带宽。
gzip 和 deflate
- HTTP请求头如果包含
Accept-Encoding: gzip,deflate
- 服务器可以对响应内容进行压缩以减少网络带宽。
ETag + Deflate
两者一起用的时候,有一个容易忽略的问题。
- ETag 通常由内容生成程序负责
- Deflate 通常由服务器负责进行
- 两种情况下理论上应该产生不同的ETag值
Apache 服务器的做法
Apache最早在启用Deflate的情况下不改变已经生成好的ETag值。 这后来被认为是一个bug:39727 ``` 对此bug的修正方法是,当Deflate启用的时候,改变ETag的值。
If-None-Match: "44d0ac3fd1f00"-gzip
可以看到,是通过简单追加 -gzip
来实现的。但这引入了一个新的bug:45023
因为理论上-gzip
应该加在引号里面。否则,根据规范,不能触发正确的内容一致性检查。
修正办法很简单,变成下面这样就可以了。
If-None-Match: "44d0ac3fd1f00-gzip"
但如果只是简单地如此修改,却还是有问题:浏览器再次发送请求时,用于检查ETag一致性的代码会认为内容不一样。因为ETag的内容来自用户的程序,此时并没有添加"-gzip"后缀,简单地与带有后缀的请求头中的值进行比较不能得出一致的结论。
修正办法,自然也很直接,就是去掉-gzip
后缀后再进行比较。但 Apache 服务器器由于某种原因一直没有修正这个问题。
The Workaround
解决问题的思路是很直接的,就是需要编辑相应的头部字段
RequestHeader edit "If-None-Match" "^(.*)-gzip$" "$1"
Header edit "ETag" "^(.*[^g][^z][^i][^p])$" "$1-gzip"
RequestHeader
修改请求头里面的If-None-MatchHeader edit
修改相应里面的ETag
更进一步
又有人发现,根据rfc2616,If-None-Match
字段是可以包含多个值的,那么把有后缀的和没后缀的都放进去好了。
RequestHeader edit "If-None-Match" '^"((.*)-gzip)"$' '"$1", "$2"'
不必要的静态文件 ETag
对于如此麻烦的ETag问题,如果只是考虑静态文件的话,其实只要简单的关掉ETag功能就好了。
FileETag: None
对于性能的损失也不用担心,因为我们还有Last-Modified
这个选项起作用。
实际上FileETag
也是根据INode MTime Size这三个的组合来生成ETag值,而通常情况下文件的变动都会引起MTime
的变化,进而引起Last-Modified
的变化。
自力更生
凡事自己能动手的,解决起来最方便。
如果服务器端的生成内容的程序可以自己判断ETag并直接生成HTTP/1.1 304 Not Modified
响应的话,这个问题也可以获得解决。