sub_filter_types必须显式声明CSS/JS的MIME类型,否则仅处理text/html;需与sub_filter同作用域,配合gzip off禁用压缩,并通过curl -I确认响应头Content-Type匹配。

sub_filter_types 必须显式声明 CSS/JS 的 MIME 类型
默认情况下 sub_filter 只处理 text/html,哪怕响应里是 .css 或 .js 文件,只要响应头中 Content-Type 不匹配,替换就完全不触发。你看到文件没变、日志没报错、但内容纹丝不动——大概率是漏写了 sub_filter_types。
正确做法是明确列出目标类型:
sub_filter_types text/css text/javascript application/javascript;- 注意
application/javascript是现代标准,部分旧后端仍返回text/javascript,建议两者都写 - 不推荐用
text/*通配——它会意外命中text/plain或text/csv,带来不可控副作用
location 块里必须同时配置 sub_filter 和 sub_filter_types
sub_filter_types 不能单独存在,它必须和 sub_filter 出现在同一作用域(http、server 或 location),且顺序无关。但如果你只在 http 块里配了 sub_filter_types,而 sub_filter 写在某个 location 里,Nginx 会静默忽略该 location 的类型声明。
典型安全写法:
location ~ \.(css|js)$ {
proxy_pass https://backend;
sub_filter 'https://cdn.old.com' 'https://cdn.new.com';
sub_filter_types text/css text/javascript application/javascript;
sub_filter_once off;
}
- 用
location ~ \.(css|js)$精确限定作用路径,避免污染 HTML 或 API 响应 -
sub_filter_once off是必须的——否则每个文件只替换第一个匹配项,CSS 中多个 URL 就只能改一个 - 别在
location /下全局配,容易误伤 JSON 接口或压缩资源
gzip 和 sub_filter 天然互斥,必须禁用压缩
sub_filter 工作在响应体解码后、发送前的内存字节流阶段,而 gzip 压缩会把整个 body 打包成二进制流。一旦启用 gzip on,Nginx 就不再提供可读文本给 sub_filter 处理——结果就是“配置全对,但什么都没换”。
解决方式只有两个:
- 在对应
location中加gzip off;(推荐) - 或全局关掉
gzip,再用gzip_static on预压缩静态文件(适合纯前端部署) - 绝对不要依赖
gzip_disable或 User-Agent 判断——它不保证绕过所有压缩路径
替换失败时优先检查 Content-Type 响应头
即使你写了 sub_filter_types text/css,如果后端实际返回的是 Content-Type: application/vnd.ms-fontobject(.eot 字体)或 text/plain; charset=utf-8(某些 CGI 脚本),sub_filter 依然跳过。最直接的验证方式是 curl 查看真实响应头:
curl -I https://yoursite.com/style.css
- 确认返回的
Content-Type确实是text/css或你声明的类型之一 - 若后端动态生成 JS 并设为
text/plain,就得把text/plain加进sub_filter_types,但要小心副作用 - 浏览器开发者工具里的 Network → Headers 显示的类型,有时是浏览器推测的,以
curl -I结果为准
真正麻烦的从来不是怎么写,而是你以为它在工作,其实从第一行响应头开始就没进入过滤流程。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











