sub_filter动态修复CDN链接需满足三条件:关闭响应压缩、显式设置sub_filter_types、关闭sub_filter_once;支持变量驱动灰度切换,但须注意路径/空格/大小写细节,并实测验证。

直接在负载均衡层用 sub_filter 动态修复后端页面里的 CDN 链接,是轻量、无侵入的方案。它不改代码、不重发包,只在响应返回前做字符串替换,关键在于配置要稳、边界要准、类型要全。
确保替换能真正生效的三个硬性条件
很多配置写了却没效果,往往卡在这三点:
-
响应不能压缩:gzip 或 brotli 压缩后的响应体,
sub_filter完全无法处理。必须在对应location中加gzip off;,同时建议加proxy_set_header Accept-Encoding "";,防止后端自作主张返回压缩内容 -
Content-Type 必须显式覆盖:默认只处理
text/html,但 CDN 链接常出现在 JS、CSS、JSON 中。需补全:sub_filter_types text/html application/javascript text/css application/json; -
必须开启全局替换:默认
sub_filter_once on,只换第一个匹配项。页面里多个src="/js/app.js"就会漏掉。务必设为sub_filter_once off;
按流量特征动态切换 CDN 域名(灰度/AB 测试)
用 Nginx 变量驱动替换值,就能实现基于 Cookie、Header 或 IP 的精准路由:
- 定义映射变量,例如识别灰度用户:
map $cookie_gray_flag $cdn_domain { "on" "https://cdn-v2.example.com"; default "https://cdn-v1.example.com"; } - 在 location 中引用:
sub_filter 'src="/static/' "src=\"$cdn_domain/static/";(注意双引号包裹、变量插值需确认 Nginx 版本支持) - 更稳妥的做法是分路径隔离:比如所有带
/gray/前缀的请求走 v2 CDN,配合rewrite ^/gray/(.*)$ /$1 break;和独立的sub_filter规则
写法细节决定成败:路径、空格、大小写
HTML 和 JSON 对空格、引号、斜杠非常敏感,替换不严谨就会误伤或失效:
- 相对路径替换示例:
sub_filter 'href="/css/' 'href="https://cdn.example.com/css/';,同时再加一条应对带空格写法:sub_filter 'href = "/css/' 'href = "https://cdn.example.com/css/'; - 绝对 URL 替换注意协议和斜杠对齐:
sub_filter 'https://old-cdn.com/' 'https://cdn-new.example.com/';,末尾斜杠必须一致,否则可能把/img/logo.png里的/img/错替成/img/logo.png - 避免大小写干扰:若后端返回混用
Src和src,可统一用小写正则替代方案(需subs_filter模块)或前端规范输出
验证与兜底建议
上线前务必实测,别只看配置语法正确:
- 用
curl -I检查响应头是否含Content-Encoding: gzip,有就说明压缩没关掉 - 打开浏览器开发者工具,查看 Network → HTML 响应体,搜索原始路径是否已全部变成 CDN 地址
- 对于复杂场景(如需提取文件名、带版本号哈希的路径),
sub_filter无能为力,建议改用 OpenResty + Lua 或前置 Node.js 代理层处理











