Nginx 默认带的 sub 模块只支持最基础的字符串替换,真正的第三方 sub 扩展(如带正则的增强版)并没有编进官方预编译包,所以很多人在配置里写 subs_filter 会直接报“unknown directive”。想在页面输出里做替换,优先用内置 sub_filter,并看清它的四个边界:不支持正则、默认只替换一次、响应被 gzip 压缩后会失效、只作用于 HTML 类响应。下面从最小示例讲到常见错误、配置与验证,照着做基本能一次跑通。

先看结论
- 官方预编译包不含第三方 sub 扩展,写 subs_filter 会报 unknown directive,改用内置 sub_filter。
- sub_filter 默认只替换第一次出现,要全替换必须加 sub_filter_once off。
- 响应被 gzip 压缩后替换会失效,需要 proxy_set_header Accept-Encoding "" 或关掉后端压缩。
- sub_filter 不支持正则,只做字面量替换,复杂改写得上第三方模块或换思路。
- 改完用 nginx -t 校验,再 reload,最后 curl 看源码确认替换真的生效。
一、先弄清 Nginx 的 sub 模块到底有哪些
前置条件:你用的是 Nginx(任意较新版本都自带标准 sub 模块),并且通过反向代理或本地托管在输出 HTML。本文示例基于官方预编译包,自己编译的版本可能额外带了增强模块,行为会不同。
Nginx 有两层“替换”能力。一层是官方标准编译进来的 ngx_http_sub_module,指令叫 sub_filter,只做简单的字面量字符串替换;另一层是社区第三方模块(常被叫做 subs_filter 或类似的增强版),支持正则、支持一次替换多处。问题是后者默认不在官方二进制里,你 apt/yum 装的 Nginx 基本都没有它。
适用边界:如果你的需求只是“把页面里的 /old/ 换成 /new/”或者把某个品牌名替换掉,sub_filter 完全够用;如果你要做正则匹配、按行号替换、或一次性改几十处,才需要考虑第三方模块。先用标准能力,别一上来就折腾编译。可以先过一遍 Nginx 入门指南 把指令体系认熟,再回来对照下面三个错误。
实操上,先安装并配置好 Nginx 环境,运行 nginx -v 命令确认版本,再用 nginx -V 检查当前编译进来的模块列表;预期能在输出里看到 --with-http_sub_module,说明标准 sub 能力已就绪。如果 reload 后报 unknown directive,先按报错定位到具体是哪条 sub 指令写错,再修复成官方 sub_filter 写法,最后用 curl 验证替换是否真的进了响应体。只有当你确实需要正则匹配、按行号替换,或一次性改几十处时,才值得去折腾第三方增强模块或改用 OpenResty;否则标准 sub_filter 足够稳,也更不容易在版本升级后突然失效。另外,sub_filter 只对 text/html 默认生效,如果你要替换 JSON 接口或别的响应类型,记得补 sub_filter_types,否则会像没生效一样——这类「看起来没替换」的坑,九成都是类型或 gzip 没关导致的。
二、三个最常见错误一次看清
2.1 错误一:写了 subs_filter 却报 unknown directive
这是高频坑。有人在 nginx.conf 里照着教程写 subs_filter,结果 reload 直接报错 unknown directive。原因就是官方包没有这个第三方指令。正确做法是用 sub_filter:
sub_filter '旧字符串' '新字符串';
sub_filter_once off;
注意大小写和拼写,第三方模块叫 subs_filter(带 s),官方指令叫 sub_filter(不带 s),两者不是一回事。配置要点:sub_filter 默认只对 text/html 类型生效,要覆盖别的类型需加 sub_filter_types。
2.2 错误二:只替换了一次,后面没动
默认 sub_filter 只替换第一次出现。页面里有多处“旧字符串”时,只有第一处变了,其余原样保留,看起来像没生效。失败处理:加 sub_filter_once off; 让它替换全部匹配项。如果只想改某一段,就要配合 sub_filter_types 或改用更精细的手段。
2.3 错误三:gzip 一开,替换全失效
这是最隐蔽的坑。后端返回的是 gzip 压缩后的字节流,Nginx 在压缩层之上做文本替换是替换不到的,所以你 curl 看到的是原始内容、但浏览器拿到的是压缩内容,替换像“没发生”。修复:在反代 location 里加 proxy_set_header Accept-Encoding ""; 让后端返回未压缩内容,或者在后端关闭对该响应的 gzip。涉及更复杂的反代链路,Nginxsysc 配置教程 里有可直接套用的片段,对照着改比凭记忆写更稳。

三、最小可用配置与验证
一个能跑通的完整示例:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Accept-Encoding "";
sub_filter 'http://old.example.com' 'https://new.example.com';
sub_filter_once off;
sub_filter_types *;
}
验证与失败处理:先 nginx -t 校验语法,看到 test is successful 再 nginx -s reload。然后用 curl -s http://你的域名 | grep 新字符串 确认替换真的进了响应体;如果 grep 不到,回头查 Accept-Encoding 有没有生效、sub_filter_once 有没有加、以及后端是否对这段响应走了别的缓存。性能提示:关闭后端压缩会增加一点传输体积,只对需要做替换的路由关即可,别全局关。

总结
Nginx 官方预编译包没有第三方 sub 扩展,配置里写 subs_filter 会报 unknown directive,应改用内置 sub_filter。它有三个硬边界:默认只替换一次(要 sub_filter_once off)、不支持正则、响应被 gzip 压缩后替换会失效(用 proxy_set_header Accept-Encoding "" 兜底)。改完用 nginx -t 校验、reload,再 curl 看源码确认。把“没有 subs_filter 就换 sub_filter”“gzip 会吞掉替换”这两点记牢,基本能避开九成相关翻车。
常见问题
Q:为什么我写 subs_filter 直接报 unknown directive?
你用的是官方预编译包,里面只有标准 sub_filter,没有第三方 subs_filter 指令。把配置改成 sub_filter,或自行编译带增强模块的 Nginx。
Q:sub_filter 只改了第一处,后面的没变?
默认只替换第一次。加 sub_filter_once off; 让它替换全部匹配项即可。
Q:本地 curl 能看到替换,浏览器里却没有?
多半是 gzip 在作怪:后端返回压缩内容,Nginx 替换不到。反代处加 proxy_set_header Accept-Encoding ""; 让后端返回明文,再试。
Q:sub_filter 能不能用正则?
官方标准版不能,只支持字面量。需要正则替换得装第三方增强模块,或改用 OpenResty/lua 等方案。
延伸学习
- 想把 Nginx 这块系统补齐,可以先跟一遍 Reactaredux 课程 里的前后端协作部分,理解配置为什么这样分层。
- 替换配置改完怎么验证是否真的生效,这篇 Nginx 相关笔记 的「先小步验证」思路可以借用。
- 写累了想就地查指令,Nginx 安装配置笔记 的速查能帮你少踩拼错指令的小坑。

TRAE-AI编程



