sub_filter能对缓存响应体实时过滤,但仅作用于解压后的明文响应流;需配置sub_filter_types、禁用gzip或启用gunzip,并确保其位于proxy_pass之后的location块内。

sub_filter 本身不处理缓存内容,它只在响应体生成后、返回客户端前实时作用于 Nginx 的输出过滤链。因此,“缓存响应内容的实时敏感词过滤”这个说法需要厘清:真正被 sub_filter 处理的,是**从缓存中读取并回源(或直接命中)后即将发出的原始响应体**,而非缓存文件本身。只要响应走的是 Nginx 的 filter 流程(即未被 bypass),sub_filter 就能介入。
确保 sub_filter 能触达缓存响应
默认情况下,Nginx 缓存(proxy_cache)响应会绕过部分 filter 模块,但 sub_filter 是 body 过滤器,只要响应未被标记为不可修改(如含 Cache-Control: no-transform),且配置位置正确,它就会生效。关键点如下:
- sub_filter 必须放在启用缓存的 location 块内,且位于 proxy_pass 之后 —— 它不是对缓存文件做预处理,而是对“缓存返回的响应流”做流式替换
- 若使用 proxy_cache_use_stale updating 等机制,需确认 stale 响应仍进入完整 filter 链;一般命中有效缓存时,sub_filter 照常工作
- 避免用 proxy_buffering off + chunked transfer encoding 组合,这可能干扰 sub_filter 的 body 分块处理逻辑
适配常见缓存响应类型
缓存内容多为 text/html、application/json 或 text/plain,而 sub_filter 默认仅处理 text/html。必须显式声明支持类型:
- 添加 sub_filter_types text/html application/json text/plain; —— 否则 JSON 接口的脱敏会静默失败
- 若缓存了 CSS/JS(不推荐),需补充对应 MIME 类型,但注意 sub_filter 不适合处理压缩或混淆后的 JS 代码
- 对二进制响应(如图片、字体)无需也不应启用 sub_filter,它们本就不含可替换文本
应对 gzip 压缩缓存的典型解法
如果上游或 Nginx 自身对缓存内容启用了 gzip(Content-Encoding: gzip),sub_filter 将完全失效 —— 因为它只处理明文。两种可靠方案:
-
方案一(推荐):服务端禁用缓存压缩 —— 在 proxy_cache_key 或后端响应头中统一移除 gzip,例如:
proxy_set_header Accept-Encoding "";(发给上游)+ gzip off;(Nginx 本地不压缩缓存响应) - 方案二:启用 gunzip 解压 —— 加入 gunzip on; 并确保编译时启用了 ngx_http_gunzip_module;该指令会对 gzip 响应自动解压后再交由 sub_filter 处理
构建安全、可控的脱敏链
缓存场景下更需注意替换顺序与幂等性,避免因多次缓存命中导致重复替换或错位:
- 用 sub_filter_once off; 确保全文所有匹配项都被处理(如手机号、身份证号可能多次出现)
- 按“长词优先、高频优先”排序多条 sub_filter,例如先替换 “张三丰” 再替换 “张三”,防止前者被后者截断
- 避免循环替换,如 sub_filter "abc" "xyz"; sub_filter "xyz" "abc"; —— 在缓存环境下该错误会被放大,造成响应异常
- 对已脱敏内容(如后端返回的 138****1234)无需二次处理,可跳过对应规则,减少 CPU 开销
不复杂但容易忽略

















