nginx 通过 header_filter 和 body_filter 在响应流中精准注入安全内容,不改后端、无延迟;header_filter 一次设置响应头,body_filter 按 last_buf 和 content_type 判断时机注入 html/json/http 头,需防 gzip 冲突、跨 buf 截断及重复注入。

巧用 Nginx 核心过滤模块,在反向代理运行期注入安全内容,关键不是“加功能”,而是把安全逻辑嵌进响应流本身——不改后端、不依赖外部服务、不增加延迟,靠 header filter 和 body filter 在内存中精准干预。
明确注入时机与模块分工
header filter 负责修改状态码、添加/覆盖响应头(如 CSP、X-Content-Type-Options),它只执行一次,适合做全局策略声明;body filter 处理实际响应体,可多次触发(因响应可能分片),必须识别 last_buf 标志才能准确在末尾注入,且要判断 buf->in_file 区分内存块或文件映射。
- 注入 HTML 脚本(如埋点 JS):仅当
buf->last_buf == 1且r->headers_out.content_type->data含text/html时操作 - 注入 JSON 安全字段(如
"_security": {"ts": 1718611200}):需先解析原始 JSON 长度,再拼接新字段,注意 UTF-8 编码完整性 - 注入 HTTP 响应头(如
X-Response-ID):直接写入r->headers_out链表,无需等待 body
确保注入可控且不破坏原有逻辑
过滤模块不可阻塞、不能调用网络或 sleep,所有内存必须从 r->pool 分配,生命周期与请求一致。重复注入是常见错误,需用 r->main_filter_ctx 或自定义 ctx 指针标记是否已处理过。
- 避免 gzip 冲突:若后端已压缩,body filter 看到的是二进制流,无法正则匹配或文本拼接;应在 gzip filter 之后注册(即
ngx_http_top_body_filter = my_filter),或统一关闭后端 gzip 改由 Nginx 统一压缩 - 防截断风险:注入内容必须完整写入单个 buffer,不能跨 buf 拆分;若原始响应末尾无换行,注入前需补空格或换行符,避免标签粘连
- 拒绝非目标响应:通过
r->headers_out.status过滤 2xx/3xx 响应,跳过 4xx/5xx 错误页,防止干扰错误提示
用 sub_filter 替代开发的轻量方案
若只需简单文本替换(如在











