You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 16:52:47