<p>OpenResty 通过 body_filter_by_lua 流式替换响应体文本最常用安全,header_filter_by_lua 修改响应头或注入内容,rewrite_by_lua* 预处理请求路径参数;需注意 chunk 截断、二进制内容保护及性能优化。</p>

OpenResty 可以通过 Lua 脚本在 请求阶段(如 rewrite_by_lua*)或 响应阶段(如 body_filter_by_lua*)对内容做动态替换,核心在于拦截并修改原始数据流。关键不是“重写整个响应体”,而是利用 OpenResty 提供的流式处理机制,在不缓存全部响应的前提下高效替换。
用 body_filter_by_lua* 替换响应体中的文本
这是最常用、最安全的方式,适合 HTML、JSON、纯文本等响应内容的关键词替换。OpenResty 将响应体分块传递给 Lua,你只需对每个 chunk 做字符串替换即可:
- 使用 body_filter_by_lua_block 或 body_filter_by_lua_file
- 通过 ngx.arg[1] 获取当前 chunk(二进制字符串),ngx.arg[2] 表示是否为最后一块
- 用 string.gsub 或 ngx.re.gsub(支持 PCRE,更强大)进行替换
- 替换后调用 ngx.arg[1] = new_chunk 覆盖原 chunk;若需丢弃,设为 ""
示例:把响应中所有 "old-api.example.com" 替换为 "new-api.prod"
body_filter_by_lua_block {
local chunk = ngx.arg[1]
if chunk ~= "" then
-- 使用 ngx.re.gsub 支持正则和大小写不敏感
local new_chunk, n, err = ngx.re.gsub(chunk, "old%-api%.example%.com", "new-api.prod")
if err then
ngx.log(ngx.WARN, "gsub failed: ", err)
else
ngx.arg[1] = new_chunk
end
end
}用 header_filter_by_lua* 修改响应头或注入内容
当需要动态添加、改写或删除 HTTP 响应头(如 Content-Security-Policy、X-Frame-Options),或向 HTML 的 <head> 中注入脚本/样式时,用这个阶段更合适:
- 它在响应头已生成但尚未发送时运行,ngx.header.* 可读可写
- 不能访问响应体,但可配合 subrequest 预取内容再决策(慎用,避免阻塞)
- 常见用途:动态设置
Cache-Control、添加X-Backend标识、根据用户 UA 注入不同 JS
示例:对移动端用户响应头中添加自定义标识
header_filter_by_lua_block {
local ua = ngx.var.http_user_agent or ""
if ua:match("Mobile") or ua:match("Android") or ua:match("iPhone") then
ngx.header["X-Device"] = "mobile"
end
}用 rewrite_by_lua* 预处理请求路径或参数(间接影响响应)
若替换逻辑依赖请求信息(如 URI、query、cookie、headers),应在请求进入 upstream 前完成判断和改写,避免后端重复计算:
- 使用 rewrite_by_lua_block 可修改 ngx.var.uri、ngx.var.args 等变量
- 可调用 ngx.exec 内部跳转,或 ngx.redirect 外部重定向
- 注意:此阶段不能读取响应,仅用于“路由级”动态干预
示例:将带 v=beta 参数的请求,自动转发到 beta 后端
rewrite_by_lua_block {
local args = ngx.req.get_uri_args()
if args.v == "beta" then
ngx.var.upstream_host = "backend-beta"
-- 同时清理 v 参数,避免透传
ngx.req.set_uri_args({v = nil})
end
}注意事项与避坑点
动态替换看似简单,但实际部署中容易出错:
-
body_filter_by_lua* 是流式执行,chunk 可能被截断(如
"hello</"+"body>"),正则跨 chunk 匹配会失败 → 应避免依赖跨块结构,或启用 lua_http10_buffering off 并结合缓冲逻辑(复杂,一般不推荐) - 二进制响应(如图片、PDF)不能直接用 string.gsub,否则会损坏文件 → 必须先判断 ngx.var.content_type,只对 text/*、application/json 等类型处理
- 替换操作有性能开销,高频大流量场景建议用 ngx.re.gsub(C 实现,比 Lua string.gsub 快数倍)
- 调试时开启 error_log /path/debug.log debug;,用 ngx.log(ngx.DEBUG, ...) 输出中间值


















