sub_filter是nginx基于字面量的流式响应体替换指令,需禁用压缩、匹配mime类型、启用缓冲;替换须精准,支持批量与动态变量,注意缓存及响应头一致性。

sub_filter 是 Nginx 内置的轻量级响应体替换指令,工作在反向代理返回内容之后、发往客户端之前。它不解析 HTML 结构,也不支持正则,而是基于字面量做流式字符串替换——正因如此,它高效、低开销,也对 CDN 域名切换、灰度发布、环境标识注入等场景特别实用。
生效前提:三件事必须做对
sub_filter 不是“配了就生效”,它有明确的运行边界:
-
响应不能压缩:gzip 或 brotli 压缩后的响应体是二进制流,sub_filter 无法处理。必须在 location 中加
proxy_set_header Accept-Encoding "";,或显式gzip off;,确保后端返回明文 -
MIME 类型要匹配:默认只处理
text/html。CDN 链接常出现在 JS、CSS、JSON 中,需手动扩展:sub_filter_types text/html application/javascript text/css application/json; -
输出缓冲要启用:sub_filter 要求响应被完整缓冲(而非流式传输),否则可能截断替换。确保
proxy_buffering on;(默认开启),必要时调大proxy_buffers
替换写法:精准匹配,避免误伤
它不智能,只认字面串。空格、引号、斜杠、大小写都算在内——写错一个字符就漏替或错替:
- 优先用完整路径前缀,比如
sub_filter 'src="/static/js/app.js"' 'src="https://cdn.example.com/static/js/app.js"';,比只替换/static/更安全 - 处理协议相对 URL:
sub_filter '//old-cdn.com/' '//cdn-v2.example.com/'; - 兼容属性值前后空格:
sub_filter 'href="/css/' 'href="https://cdn.example.com/css/';和sub_filter 'href = "/css/' 'href = "https://cdn.example.com/css/';可同时配置 - 大小写不一致时加
sub_filter_ignore_case on;,但会略降性能
批量与动态:让一次配置适配多套 CDN
默认 sub_filter_once on; 只换第一个匹配项,页面里多个资源引用就失效了:
- 必须设为
sub_filter_once off;才能全局替换 - 想按 Host、Cookie 或请求头切换 CDN 域名,用
map预定义变量:map $host $cdn_host {<br> site-a.com "https://cdn-a.example.com";<br> site-b.com "https://cdn-b.example.com";<br> default "https://cdn-default.example.com";<br> }
再在 location 中写:sub_filter '/static/' "$cdn_host/static/"; - 若 Nginx 版本低于 1.11.6,变量插值可能不支持,可用
if+set模拟,或拆成多个 location 分流
缓存与响应头:别让替换引发副作用
替换后响应体已变,但原始响应头没更新,容易出问题:
-
Content-Length会自动移除,改用 chunked 编码——这是正常行为,但若下游系统依赖该头,需提前评估 - 务必加
sub_filter_last_modified off;,否则修改内容后Last-Modified头仍指向旧时间,导致缓存校验失败 - ETag 由原始响应生成,替换后值已不匹配。建议
etag off;或配合add_header ETag "";清除 - 若用
proxy_cache,且希望不同 CDN 域名走不同缓存副本,需在proxy_cache_key中加入$cdn_host等变量











