sub_filter仅做纯文本替换,不解析JSON结构,直接用于JSON易破坏格式;应改用OpenResty+Lua安全重写字段,或由后端动态生成URL。

sub_filter 指令本身不支持解析 JSON 结构,也不能安全修改 JSON 内部字段值(如 URL 字段),因为它仅做**纯文本替换**。直接用 sub_filter 替换 JSON 中的链接,极易破坏 JSON 格式(比如误改引号、逗号、转义符或字段名),导致前端解析失败。因此,真正的可行方案是:**避免用 sub_filter 处理 JSON 内容体;改用更安全、语义明确的方式在代理层重写链接。**
明确 sub_filter 的适用边界
sub_filter 适合处理明确、固定、非结构化的文本内容,例如:
- HTML 响应中硬编码的静态资源路径(
<script src="/js/app.js"></script>→<script src="https://cdn.example.com/js/app.js"></script>) - 纯文本响应里的域名替换(
http://internal-api/→https://api.example.com/) - 且需配合
sub_filter_once off和sub_filter_types application/json才会作用于 JSON 响应体
但它无法识别 JSON 键名、值边界或嵌套结构——你不能靠它“只替换 "avatar_url" 字段的值,而不碰 "backup_url" 或注释里的相似字符串。
推荐替代方案:用 Nginx + Lua(OpenResty)精准重写 JSON 字段
若后端返回标准 JSON(如 {"user":{"name":"A","avatar_url":"http://internal/uploads/123.jpg"}}),且需将其中特定字段的 URL 动态替换为公网地址,应使用 ngx_lua 模块(需部署 OpenResty 或编译含 Lua 支持的 Nginx):
- 用
content_by_lua_block或body_filter_by_lua_block拦截响应体 - 调用
cjson.decode()安全解析 JSON - 遍历或定位目标字段(如递归查找所有
xxx_url键),用正则或字符串替换更新值 - 再用
cjson.encode()序列化回合法 JSON,设好 Content-Length
示例片段(简化):
body_filter_by_lua_block {
local chunk = ngx.arg[1]
if ngx.arg[2] then -- last chunk
local json = ngx.ctx.json_data or ""
json = json .. chunk
local ok, obj = pcall(cjson.decode, json)
if ok and type(obj) == "table" then
local rewrite_url = function(val)
return string.gsub(val, "^http://internal%-api/", "https://api.example.com/")
end
-- 递归重写所有 *url 字段(可按需定制逻辑)
local function walk(t)
for k,v in pairs(t) do
if type(v) == "string" and k:match("url$") then
t[k] = rewrite_url(v)
elseif type(v) == "table" then
walk(v)
end
end
end
walk(obj)
ngx.arg[1] = cjson.encode(obj)
end
else
ngx.ctx.json_data = (ngx.ctx.json_data or "") .. chunk
end
}轻量级折中:后端配合 + 反向代理头注入
如果无法引入 Lua,最稳妥的做法是让后端承担链接生成责任:
- 后端读取请求头(如
X-Forwarded-Proto、X-Forwarded-Host)动态构造资源 URL - Nginx 代理时透传必要头:
proxy_set_header X-Forwarded-Host $host;、proxy_set_header X-Forwarded-Proto $scheme; - 后端根据这些头拼出正确公网 URL(如
https://$host/uploads/xxx),返回给前端
这种方式零 JSON 解析风险,符合 REST 设计原则,也便于 CDN 缓存与灰度发布。
实在要用 sub_filter?必须满足三个前提
仅当同时满足以下条件时,才可谨慎尝试 sub_filter 替换 JSON 内链接:
- JSON 结构极简单、扁平(无嵌套对象/数组)、字段名和 URL 格式高度固定
- URL 出现在明确引号包裹的独立字段值中,且前后无干扰字符(如
"image_url": "http://internal/a.png") - 启用严格匹配:用
sub_filter 'http://internal/' 'https://cdn.example.com/';并确保源 URL 不会出现在键名、数字或注释中
务必开启 sub_filter_last_modified on; 和 sub_filter_once off;,并用 curl -i 验证响应头 Content-Length 是否被自动修正(否则易截断)。











