NGINX 反向代理导致客户端缓存标记丢失的两种情况

NGINX 反向代理导致客户端缓存标记丢失的两种情况

发表于 2022/08/12 更新于 2022/08/12 446 字 2 分钟
AI 摘要 由 AI 自动生成

|

问题引入

遇到一个很奇怪的问题,后端站点使用 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

    ngx_str_set(&h->key, "Content-Encoding");
    ngx_str_set(&h->value, "gzip");
    r->headers_out.content_encoding = h;
    r->main_filter_need_in_memory = 1;
    ngx_http_clear_content_length(r);
    ngx_http_clear_accept_ranges(r);
    # remove etag
    ngx_http_clear_etag(r);

自 NGINX 1.7.3 版本以后,当在 gzip 中遇到 etag 头,会将强 etag 自动转换为弱 etag(weak etag),如果遇到弱 etag,则不作处理原样返回。

源码分析:./release-1.7.3/src/http/modules/ngx_http_gzip_filter_module.c

    ngx_str_set(&h->key, "Content-Encoding");
    ngx_str_set(&h->value, "gzip");
    r->headers_out.content_encoding = h;
    r->main_filter_need_in_memory = 1;
    ngx_http_clear_content_length(r);
    ngx_http_clear_accept_ranges(r);
    # 弱化 etag 标记
    ngx_http_weak_etag(r);

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

if (r == r->main) {
        ngx_http_clear_content_length(r);
        ngx_http_clear_etag(r);
        if (!slcf->last_modified) {
            ngx_http_clear_last_modified(r);
        }
    }

自 NGINX 1.7.3 优化了 sub_filter 模块对 etag 和 last-modified 的处理逻辑。

源码分析:./release-1.7.0/src/http/modules/ngx_http_sub_filter_module.c

if (r == r->main) {
        ngx_http_clear_content_length(r);
        # sub_filter_last_modified off
        if (!slcf->last_modified) {
            ngx_http_clear_last_modified(r);
            ngx_http_clear_etag(r);
        # sub_filter_last_modified on
        } else {
            ngx_http_weak_etag(r);
        }
    }

clear 处理函数分析

以 nginx-release-1.7.0 为例,会将 hash 置 0,标记置空。

源码位置:./release-1.7.3/src/http/ngx_http_core_module.h

#define ngx_http_clear_last_modified(r)                                       \
                                                                              \
    r->headers_out.last_modified_time = -1;                                   \
    if (r->headers_out.last_modified) {                                       \
        r->headers_out.last_modified->hash = 0;                               \
        r->headers_out.last_modified = NULL;                                  \
    }

#define ngx_http_clear_etag(r)                                                \
                                                                              \
    if (r->headers_out.etag) {                                                \
        r->headers_out.etag->hash = 0;                                        \
        r->headers_out.etag = NULL;                                           \
    }

建议

1、后端使用客户端强缓存(例如 cache-control)方式能避免该问题。 2、调整 NGINX 版本,避免 NGINX 版本特性。

作者: 小谈谈
声明: 本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。