启用 sub_filter_once off 是实现网页关键词全局替换与脱敏的核心前提——它让 Nginx 扫描整个响应体并对所有匹配项逐一处理,但需同时满足 Content-Type 匹配、响应未压缩、响应体不超 buffer 限制三个条件。

启用 sub_filter_once off 是实现网页关键词全局替换与脱敏的核心前提——它让 Nginx 不再只替换响应中第一个匹配项,而是扫描整个响应体(在 buffer 容量内),对所有符合条件的字符串逐一处理。
必须满足的三个基础条件
即使写了 sub_filter_once off,若以下任一条件不满足,替换仍会失效或仅部分生效:
-
Content-Type 必须匹配:Nginx 默认只处理
text/html,若需替换 JS、CSS 或 JSON 中的关键词(如 API 地址、用户 ID、手机号),必须显式声明:sub_filter_types text/html application/javascript text/css application/json text/plain; -
响应不能被压缩传输:gzip/brotli 压缩后的内容无法被 sub_filter 直接读取。推荐做法是让后端关闭压缩,并在 Nginx 中加:
proxy_set_header Accept-Encoding "";
若后端强制返回 gzip,且你已编译ngx_http_gunzip_module,可启用:gunzip on; -
响应体不能超出 buffer 限制:默认 sub_filter 内部 buffer 约 1MB。若页面超大(如含长日志、大量内联 JS),可能漏掉尾部内容。可调大:
sub_filter_buffer_size 4m;
关键词全局替换的典型配置写法
以下配置放在 location 块中,适用于反向代理或静态托管场景:
sub_filter 'user@example.com' '[email redacted]'; sub_filter '138****1234' '[phone redacted]'; sub_filter '订单号: ORD-' '订单号: [ID masked]-'; sub_filter_once off; sub_filter_types text/html application/javascript text/css application/json text/plain; # 若代理后端,务必禁用压缩传递 proxy_set_header Accept-Encoding ""; # 可选:避免 Last-Modified 头被意外清除影响缓存 sub_filter_last_modified off;
注意:多条 sub_filter 按书写顺序依次执行,若存在嵌套(如先替换了“ABC”,再替换“AB”),结果可能不可预期,建议保持关键词无包含关系。
敏感信息脱敏的实用技巧
脱敏不是简单替换,需兼顾可读性与安全性:
-
手机号/身份证号等结构化数据:用固定掩码格式,例如将
13912345678替换为139****5678;Nginx 不支持正则,所以需确保原始 HTML 中该字段格式统一,或由前端预留标准占位符(如__PHONE__)再由 Nginx 替换 -
动态域名/接口地址注入:配合
map指令实现环境区分,例如:map $host $api_domain { ~\.staging\. example-staging.com; default example-prod.com; }
再写:sub_filter '__API_HOST__' "$api_domain"; -
避免误伤 HTML 标签或属性值:Nginx sub_filter 是纯字符串匹配,不解析 DOM。若关键词出现在
href、data-属性或注释中,也会被替换。建议关键词设计带明确边界,如用双下划线包裹:__USER_EMAIL__,降低冲突概率
验证与排错要点
替换失败常见于看不见的环节:
- 用
curl -I检查响应头:Content-Type是否在sub_filter_types列表中,Content-Encoding是否为空(非gzip) - 用
curl -s获取原始响应体,确认待替换关键词确实存在、拼写完全一致(区分大小写) - 若使用变量(如
$host),确认 Nginx 版本 ≥ 1.9.11 且编译时启用了变量支持;不支持时改用set+if预赋值 - 修改配置后执行
nginx -t && nginx -s reload,不要只重载而不校验语法

















