可行但有明确边界:仅适用于简单静态明文替换,不支持正则捕获、压缩内容或html结构解析;需配置sub_filter_types、gunzip on或禁用后端gzip,并规避循环替换与缓冲区限制。

直接在 Nginx 负载均衡层用 sub_filter 做敏感词脱敏,可行但有明确边界:它只适合简单、静态、明文的字符串替换,不能做正则捕获、不处理压缩内容、不解析 HTML 结构。关键在于配置精准、规避限制。
确保响应可被 sub_filter 处理
sub_filter 只对指定 MIME 类型且未压缩的响应生效:
- 必须显式设置
sub_filter_types,例如text/html text/plain application/json;仅靠默认值(text/html)会漏掉 API 返回的 JSON 或纯文本日志 - 若后端启用了 gzip(常见于现代 Web 服务),Nginx 默认不会解压,
sub_filter将完全跳过。需添加gunzip on;(前提是编译时启用了ngx_http_gunzip_module),或让后端关闭 gzip 响应 - 避免代理头干扰:建议加
proxy_set_header Accept-Encoding "";,主动告知后端“我不接受压缩”,从源头规避解压问题
配置多级替换与顺序控制
一个 location 中允许多条 sub_filter 指令,Nginx 按书写顺序逐条执行,可构建基础脱敏链:
- 把长词、高频词放在前面,减少后续匹配的字符串长度和次数,提升效率
- 避免循环替换:例如
sub_filter "abc" "xyz"; sub_filter "xyz" "abc";会导致不可控结果,务必检查替换后的内容是否可能触发下一条规则 - 手机号、身份证等固定格式可用掩码字符串直接替换,如
sub_filter "13812345678" "138****5678";;但注意这依赖原始内容中已含完整明文,无法自动识别并脱敏任意手机号
结合 proxy_pass 实现动态注入与清理
在反向代理场景中,sub_filter 最实用的不是“找词”,而是“改标识”或“删调试内容”:
- 替换测试环境水印:
sub_filter '<div class="env">TEST</div>' ''; - 移除前端调试脚本:
sub_filter '<script src="/debug.js"></script>' ''; - 统一域名回写:
sub_filter 'https://prod.example.com' '$scheme://$host';(需开启sub_filter_once off) - 所有替换都需配合
sub_filter_once off实现全局替换,否则只改第一个匹配项
认清局限,必要时换方案
sub_filter 不是万能过滤器:
- 不支持大小写不敏感匹配(需第三方模块或后端处理)
- 不支持正则捕获组重用(如想把任意手机号替换成统一掩码格式,它做不到)
- 对大响应体存在缓冲区限制,超长内容可能被截断处理
- 真正需要识别并脱敏任意格式敏感信息(如所有 11 位数字串、所有 18 位含 X 身份证),应交由 WAF 的防敏感信息泄露功能,或在应用层用 DFA/Trie 算法实现











