如何用Nginx njs模块为上游响应添加基于响应体的自定义头?
问题描述
我希望在Nginx中,基于上游服务器的响应体添加自定义响应头(为简化测试,暂设为响应体的SHA1哈希)。尝试使用Nginx的njs脚本模块,参考了njs-examples仓库中的示例,但这些示例分别处理头部和体部分,难以将二者结合以实现目标。
当前配置如下:
Nginx配置文件(nginx.conf)
load_module modules/ngx_http_js_module.so; events {} http { js_path "/etc/nginx/njs/"; js_import main from hello.js; # 应用服务器列表配置 upstream app_servers { server flask:5000; } server { listen 80; server_name localhost; location / { js_body_filter main.hello; proxy_pass http://app_servers/; } } }
njs脚本(hello.js)
function hello(r, data, flags) { var val = data.length; ngx.log(1, val); r.headersOut["X-Hello"] = val; r.sendBuffer(data, flags); } export default { hello };
发送请求后,响应中并未出现X-Hello头,请问是否可以用Nginx的njs脚本实现需求?如果不能,应该考虑哪些替代方案?
解决方案
为什么当前配置不生效
js_body_filter在响应体分块传输阶段被调用,此时Nginx已经把响应头发送给客户端了,后续修改r.headersOut不会生效——响应头的发送时机早于体过滤的执行时机。
用njs实现的正确方式
要实现基于响应体生成自定义响应头,必须先缓存整个响应体,计算出哈希值后再发送响应头和响应体。可以通过js_filter(区别于js_body_filter)在过滤流程中缓存数据,最后在eof(数据结束)标志触发时完成计算并设置响应头。
修改后的njs脚本(hello.js)
function bodyHashFilter(r, data, flags) { // 初始化缓存,存储响应体分段数据 if (!r.bodyCache) { r.bodyCache = []; } // 非最后一块数据,加入缓存暂存 if (!flags.last) { r.bodyCache.push(data); return; } // 处理最后一块数据,合并所有缓存内容 r.bodyCache.push(data); const fullBody = Buffer.concat(r.bodyCache); // 计算响应体的SHA1哈希(njs内置crypto模块支持) const sha1 = require('crypto').createHash('sha1'); sha1.update(fullBody); const hash = sha1.digest('hex'); // 设置自定义响应头 r.headersOut['X-Response-Body-SHA1'] = hash; // 发送完整响应体给客户端 r.sendBuffer(fullBody, flags); } export default { bodyHashFilter };
修改后的Nginx配置
将location中的js_body_filter替换为js_filter:
location / { js_filter main.bodyHashFilter; proxy_pass http://app_servers/; }
注意:这种方式会缓存整个响应体,对于大体积响应会占用较多内存,需要根据业务场景评估是否适用。
替代方案
如果njs的内存开销无法满足需求,可以考虑以下方案:
- 自定义Nginx C模块:直接在Nginx核心层处理,内存控制更灵活,但需要C语言开发能力,维护成本较高。
- 上游服务配合:让上游应用直接计算响应体哈希并添加响应头,Nginx仅做转发,这种方式最轻量化,无需Nginx额外处理。
- 使用OpenResty的Lua模块:OpenResty的
lua_body_filter可以实现类似逻辑,Lua的内存管理相对灵活,社区资源也更丰富。
内容的提问来源于stack exchange,提问作者Gaurav Jain
相关产品推荐
相关产品推荐

