NGINX 反向代理导致客户端缓存标记丢失的两种情况
问题引入
遇到一个很奇怪的问题,后端站点使用 etag 和 last-modified 为静态资源增加了客户端缓存,经过 NGINX 代理之后,etag 和 last-modified 标记到客户端就消失了,导致客户端无法使用浏览器缓存。
排查和结论
1、是否因为 gzip 导致 etag 丢失,在某些版本(1.3.3~1.7.3)的 NGINX 中,当开启 gzip 时,会导致 etag 头丢失。
官方给出的解释是压缩以后,文件大小可能被修改,会导致文件发生变化,故考虑将 etag 头丢掉。
源码分析:./release-1.3.3/src/http/modules/ngx_http_gzip_filter_module.c
1 | ngx_str_set(&h->key, "Content-Encoding"); |
自 NGINX 1.7.3 版本以后,当在 gzip 中遇到 etag 头,会将强 etag 自动转换为弱 etag(weak etag),如果遇到弱 etag,则不作处理原样返回。
源码分析:./release-1.7.3/src/http/modules/ngx_http_gzip_filter_module.c
1 | ngx_str_set(&h->key, "Content-Encoding"); |
2、是否因为引入了 sub_filter 模块导致的 etag 头丢失,自 1.5.1 版本起 NGINX 引入了一个配置项允许在替换期间保留原始响应中的 last-modified 头字段,以便于响应缓存。但默认情况下,在 sub_filter 模块处理过程中修改响应内容时,将删除 last-modified 头字段。
配置项目 sub_filter_last_modified on | off;
默认值 sub_filter_last_modified off;
可出现的位置 http,server,location
源码分析:./release-1.5.11/src/http/modules/ngx_http_sub_filter_module.c
1 | if (r == r->main) { |
自 NGINX 1.7.3 优化了 sub_filter 模块对 etag 和 last-modified 的处理逻辑。
源码分析:./release-1.7.0/src/http/modules/ngx_http_sub_filter_module.c
1 | if (r == r->main) { |
clear 处理函数分析
以 nginx-release-1.7.0 为例,会将 hash 置 0,标记置空。
源码位置:./release-1.7.3/src/http/ngx_http_core_module.h
1 |
建议
1、后端使用客户端强缓存(例如 cache-control)方式能避免该问题。
2、调整 NGINX 版本,避免 NGINX 版本特性。