联系支持

我们通过电子邮件回复,通常两天内。

为防止滥用,Google reCAPTCHA 会检查这次提交,其间会向 Google 传输数据。脚本只在你打开此表单时才加载。

← 全部文章

每一次都把同一个文件压一遍

Apache 是即时压缩响应的。对一个每次都不一样的页面来说,这是唯一的办法。对一个一个月才改一次的样式表来说,这意味着服务器为每一位访客把同样的活儿重做一遍,一做几个星期,而且每次都把结果扔掉。这件事没有缓存。

2026 年 8 月 19 日在一条已建立的连接上实测,用的是本站自己的 style.css(124 kB)和 world.js(102 kB):

时间字节
不压缩2,9 毫秒124 755
gzip,即时18,1 毫秒34 995
brotli,即时14,5 毫秒32 685
world.js,gzip47,9 毫秒38 283
world.js,brotli15,9 毫秒38 086

每个请求四十八毫秒的 CPU,只为得到一个逐字节完全相同的结果。在树莓派上,这不是舍入误差。

压一次,压到磁盘上

一个构建步骤把压好的文件写在原文件旁边,一条重写规则在浏览器声明接受 gzip 时把它送出去。送一个现成的文件,花的就是送任何文件的成本。

用 gzip 而不用 brotli,理由很无趣:这台机器上没装 brotli 命令。文件因此比 brotli 压出来的大约 7 %,而服务器时间只有即时 brotli 的五分之一。这笔交换值得现在就做,而不是等一个更漂亮的答案。

版本号写在文件名里

构建写出的是 style.css.<mtime>.gz,而页面里的 ?v= 就是同一个修改时间。只有当带着对应数字的文件存在时,重写规则才会触发。

这个安排有一个值得刻意去设计的性质:如果你改了样式表却忘了重新构建,页面里的数字指向一个不存在的文件,规则不触发,Apache 就照常把原文件送出去。最坏的情况是加速没了。一份过期的样式表出不去。任何缓存方案都该拿这个问题检验一遍——出问题的时候,它是变慢,还是变错?

中间那个缺陷

被送出的文件本身已经是 gzip,所以出门时不能再压一遍。配置里最初只写了 no-gzip,而它只拦住了 Apache 两个压缩器中的一个。另一个,mod_brotli,兴高采烈地把压好的 gzip 文件又用 brotli 压了一遍,而响应头还一直声称是 gzip

浏览器拿到的是读不出来的内容。发现它靠的不是看页面——靠的是把字节加起来,发现有个数字说不通。两个都需要,no-gzipno-brotli,而只有其中一个是显而易见的那个。

广告