压缩通过减少http响应的大小来减少响应时间。如果Http请求得到更小的响应,服务器和浏览器之间就会有更少的包被传送,传输时间就减少了。

压缩是怎么工作的

从Http/1.1开始,浏览器通过在http请求头中添加Accept-Encoding头来支持压缩;

Accept-Encoding: gzip,deflate

如果web服务器从请求头中发现Accept-Encoding头,它可能使用Accept-Encoding指定的压缩算法列表中的一个来压缩响应。web服务器通过响应头中的Content-Encoding头来通知浏览器响应内容使用了哪种压缩算法;

Content-Encoding:gzip

Gzip是目前最流行而且很有效率的压缩方法。另外一种压缩算法是deflate,但是它稍微低效点并且不是很流行,支持deflate的浏览器也支持gzip,但是有些支持gzip的浏览器不支持deflate,所以gzip更适合作为压缩响应的方法。

哪些需要被压缩

很多网站压缩他们的HTML文档,但是压缩你的scripts脚本和stylesheet样式文件是值得的(事实上压缩任何text类型的响应,包括XML和JSON都是是值得的),图片和PDF文件不应该被压缩,因为他们已经是压缩的了,试着压缩他们会浪费CPU资源而且可能潜在增加文件大小。

压缩有一项成本:它会带来额外的服务器端压缩和客户端解压缩的CPU资源。为了权衡利弊,你需要去考虑响应的大小,网络的带宽,以及服务器和浏览器之间的网络距离等因素,这些信息通常是不容易获取的。所以,一般对于超过1k或者2k的文件都是值得去压缩的。

压缩率

压缩通常能减少响应70%左右的大小

代理缓存

当浏览器发送经过代理的请求,情况相对会变得复杂。假设来自不支持gzip的浏览器发起第一次请求到代理服务器,因为是第一次请求,所以代理服务器的缓存是空的,代理把请求转发给web服务器,web服务器作一个未压缩的响应。这个未压缩的响应被代理缓存并且发送给浏览器。现在,假第二个支持gzip压缩的请求发送到代理,代理从缓存中取出未压缩的数据作为响应,这样就丢失了使用gzip的能力。

更糟糕的是,如果第一次请求来自支持gzip的浏览器而第二次请求来自一个不支持的浏览器,这种情况下,代理缓存中有一个内容的压缩版本并且为不管是否支持gzip压缩的浏览器都提供服务。

解决这个问题的方法是在你的web服务响应头中添加一个Vary header字段,web服务器通知代理基于一个或多个请求头来做不同的缓存响应。因为是否压缩是基于Accept-Encoding头,所有有理由在web服务器的响应的vary头字段中包含Accept-Encoding

Vary: Accept-Encoding

这会导致代理会基于请求头Accept-Encoding字段的每个一个值缓存不同的响应内容版本。在我们之前的例子中,代理将缓存两个每个响应的两个版本:当Accept-Encoding是gzip时的压缩内容,和Accept-Encoding完全没有指定时的非压缩版本。当浏览器发起一个带Accept-Encoding:gzip的请求到代理,代理会取缓存中压缩的版本并作为响应到浏览器,如果没有Accept-Encoding:gzip头,浏览器将受到未压缩的版本。

代理边缘情况

虽然有90%的浏览器声称支持gzip,但是可能其中有些浏览器在解压方面存在bug。一个比较安全的方法是向被证实支持压缩的浏览器才提供压缩内容的服务,比如IE6+和Mozilla 5.0+

web服务器可以通过配置User-Agent的类型条件来对指定压缩安全的浏览器提供压缩支持,比如Apache 1.3设置如下

mod_gzip_item_include reqheader "User-Agent: MSIE [6-9]"
mod_gzip_item_include reqheader "User-Agent: Mozilla/[5-9]"

添加缓存混合这些浏览器的边缘情况,最好的方法是添加User-Agent到Vary头中

Vary: Accept-Encoding,User-Agent

这样代理就会根据请求的Accept-Encoding和User-Agent的值来缓存不同的web服务器响应版本,便于下次请求直接作出响应。但是代理基于Accept-Encoding和User-Agent请求头的组合缓存所有请求URL的响应内容,是不太可能的。

这个问题实质上是平衡压缩和缓存两者的问题,怎么处理还是得依据网站的实际情况:

  • 如果你的网站有比较少的用户并且他们是比较小众的(比如都使用firefox1.5),边缘情况就可以几乎不考虑,压缩你的内容并且使用Vary:Accept-Encoding,这样就通过减少响应大小并且使用代理缓存来增强了网站的性能。
  • 如果你更关心节省网络带宽,同样压缩你的内容和使用Vary:Accept-Encoding。这样减少了web服务器的网络带宽并且增加了被代理处理的请求数量
  • 如果你有一个大的不同的用户,能够负担得起比较高的带宽开销和一个高质量的口碑,压缩你的内容和使用Cache-Control:Private。这个会使代理不再缓存,但是避免了边缘情况的bug