nginx 的 sub_filter 指令可在反向代理层实时替换 html/css/js 中的静态资源路径以接入 cdn,需配置 sub_filter_once off、sub_filter_types、禁用 gzip,并注意 mime 类型匹配、缓存兼容性及 xss 防护。

可以通过 Nginx 的 sub_filter 指令在反向代理(负载均衡)层,对后端返回的 HTML、CSS、JS 等文本响应内容进行实时字符串替换,从而将页面中硬编码的静态资源路径(如 /static/xxx.js 或 http://origin.example.com/images/logo.png)动态改写为 CDN 域名(如 https://cdn.example.com/static/xxx.js),实现无需修改后端代码的 CDN 接入。
启用 sub_filter 并配置基础替换规则
Nginx 默认不启用 sub_filter,需确保模块已编译(1.9.4+ 内置,无需额外加载)。在 location 块中配置替换逻辑,注意必须配合 sub_filter_once off(默认为 on,只替换首次匹配)才能批量处理多个资源链接:
- 用
sub_filter "http://app.example.com/" "https://cdn.example.com/";替换绝对 URL - 用
sub_filter "/static/" "https://cdn.example.com/static/";替换相对路径(需确保响应头含Content-Type: text/html或明确匹配类型) - 添加
sub_filter_types text/html text/css application/javascript;扩展支持的 MIME 类型
处理响应头与缓存兼容性问题
sub_filter 会修改响应体,导致原始 Content-Length 失效,Nginx 自动移除该头并启用 chunked 编码。若后端或客户端依赖 Content-Length,需注意兼容性。同时,替换后的响应应避免被缓存为“原始版本”:
- 添加
proxy_set_header Accept-Encoding "";禁用后端 Gzip,确保 sub_filter 能处理明文 - 使用
proxy_cache_bypass $args;或设置独立 cache key,防止带 CDN 域名的响应被错误缓存 - 必要时加
add_header X-Sub-Filtered "true";便于调试是否命中替换逻辑
适配多环境与安全约束
生产中常需区分测试/灰度/正式环境,避免误替换;同时要防范 XSS 风险(如用户输入被渲染为 HTML 后触发替换):
- 用
map指令按 Host 或请求头动态控制是否启用替换,例如:map $host $enable_subfilter {<br> default 0;<br> ~^www\.example\.com$ 1;<br>}
再在 location 中用sub_filter ... if=$enable_subfilter; - 避免替换用户可控字段(如评论区 HTML),可通过
sub_filter_last_modified on;仅对带Last-Modified头的响应生效,通常静态资源才有 - 敏感路径(如
/admin/)建议排除,用location ^~ /admin/ { sub_filter "" ""; }显式禁用
验证与常见陷阱
替换失败常因 MIME 类型不匹配、压缩未关闭或正则表达式误用。调试建议:
- 用
curl -H "Accept-Encoding: identity" http://lb.example.com/page.html绕过 gzip,确认原始响应可读 - 检查 Nginx error log 是否报
sub_filter: no replacement found,说明未命中匹配内容 - 注意 HTML 属性值中的引号:替换
src="/a.js"时,正则需覆盖单双引号,推荐用sub_filter 'src="/' 'src="https://cdn.example.com/';并保持引号一致 - 避免跨行匹配 ——
sub_filter不支持多行,确保目标字符串在单行内(如把<script src="</code"> 和路径写在同一行)</script>











