Nginx 的 ngx_http_sub_module 支持对 HTML/JS/CSS 响应体做轻量级字面量替换,需启用模块、禁用压缩与分块传输,并通过 sub_filter、sub_filter_once、sub_filter_types 配合实现域名替换、脚本注入等。

在 Nginx 作为反向代理网关的场景中,有时需要对后端返回的 HTML、JS 或 CSS 内容做轻量级动态修改——比如替换域名、注入脚本、修正相对路径等。Nginx 原生模块 ngx_http_sub_module(即 sub_filter)提供了低开销、高并发的响应体文本替换能力,无需上游配合或应用层改造。
启用 sub_filter 模块与基础语法
Nginx 默认可能未编译该模块,需确认配置中存在 --with-http_sub_module。启用后,核心指令有三个:
-
sub_filter string replacement:定义一对替换规则,支持正则?不支持——仅字面量匹配(大小写敏感) -
sub_filter_once on|off:默认on,只替换每块响应中的首次匹配;设为off可全局替换所有出现位置 -
sub_filter_types mime-type ...:指定哪些 MIME 类型响应启用替换,默认仅text/html,务必显式添加application/javascript、text/css等
常见实用替换场景与写法
实际使用中,注意响应必须是可缓存的、非分块传输(chunked)且未压缩(gzip/brotli 需先禁用或在压缩前处理)。典型用法包括:
- 将后端内网地址
http://192.168.1.100:8080替换为公网域名https://api.example.com:sub_filter 'http://192.168.1.100:8080' 'https://api.example.com'; - 在 HTML
中注入监控脚本(需配合sub_filter_once off和精准定位):sub_filter '' '<script src="/monitor.js"></script>'; - 修正静态资源路径,如把
/static/改为带 CDN 前缀:sub_filter '/static/' 'https://cdn.example.com/static/';
关键限制与规避技巧
sub_filter 是流式处理,不解析 HTML 结构,因此易受边界干扰。常见问题及对策:
-
跨 chunk 匹配失败:若关键词被分块边界截断(如
"http://"在一个 chunk 末尾,"192.168..."在下一个),替换失效。解决方法是关闭响应压缩、确保后端不使用 chunked 编码,或改用更短/更唯一的替换关键词 -
大小写与空格敏感:HTML 中属性值可能含多余空格或大小写混用。建议统一后端输出格式,或用多次
sub_filter覆盖常见变体(如同时替换href="和href = ") -
无法正则、无法条件判断:不支持捕获组或 if 逻辑。复杂需求应考虑 Lua(via
nginx-lua-module)或改由后端模板处理
完整配置示例(含安全与健壮性)
以下是一个生产就绪的片段,用于替换 API 地址并注入脚本:
location / {
proxy_pass https://backend;
proxy_set_header Host $host;
<pre class="brush:php;toolbar:false;"># 关键:禁用压缩以保障 sub_filter 可靠性
proxy_set_header Accept-Encoding "";
gzip off;
gzip_static off;
# 启用对 HTML、JS、CSS 的替换
sub_filter_types text/html text/css application/javascript;
sub_filter_once off;
# 多次替换覆盖常见写法
sub_filter 'http://internal-api/' 'https://api.example.com/';
sub_filter 'https://internal-api/' 'https://api.example.com/';
sub_filter '' '<script async src="https://cdn.example.com/inject.js"></script>';
# 防止缓存污染:强制不缓存被修改的响应
add_header X-Sub-Filtered "true";
expires -1;
}










