nginx开启gzip模块后ETAG丢失

为节省网页的传输时间,页面生成的时候,后端 PHP 给 nginx 返回了 ETAG 这个 HTTP 头。

这样一来,如果页面的内容没有变化的话,通过直接返回304可以大大地节省所需的传输时间,从而实现某种程度上的加速。

本来这个流程是可以正常工作的,但是突然某一天发现,页面不再有304这个状态了,无论怎么刷新,内容没有变化一样是200。

通过查看 HTTP 头发现,服务器根本没有返回 ETAG 这个头,而 PHP 却是确定有返回 ETAG 的,这就奇怪了。

一切的证据都指向 nginx 弄丢了 ETAG 这个 HTTP 头。

Google 一下,果然是这样,nginx 在开启了 gzip 之后,如果有 ETAG 则会调用 ngx_http_clear_etag 将其清除。

nginx开启gzip模块后ETAG丢失 图1

而同时 Google 得知,从 nginx 1.7.3 这个版本之后,nginx 不再强硬地清除 ETAG 了,而是换了一种 weak ETAG 的策略。

这个策略就是,如果 PHP 返回了一个 weak ETAG,则 nginx 不处理直接返回给客户端,如果 PHP 返回了强 ETAG,则 nginx 会将其转换为 weak ETAG。ngx_http_core_module.c 中约 1854行 的 ngx_http_weak_etag 函数完成了这个过程。

nginx开启gzip模块后ETAG丢失 图2

解决的办法很简单,只要 PHP 返回的 ETAG 是 weak ETAG,那么就一切都会正常起来了。

而所谓的 weak ETAG,也就是弱 ETAG,它是相对于正常 ETAG 而言的,表现形式就是 ETAG 前面加上 W/,例如:

ETAG: zhetenga.com 这是正常 ETAG,ETAG: W/zhetenga.com 就是 weak ETAG(弱 ETAG),ETAG保证文件内容完全相同,而 weak ETAG 则只保证文件内容是意义上相同的,例如 gzip 过了。

2015年05月26日