NGINX njs 全解析:从基础配置到高级特性实战

一、njs 核心基础:模块与配置入门

1. 模块加载与核心定位

ngx_http_js_module 是 njs 的核心模块,需先在 NGINX 配置中加载(编译 NGINX 时需启用该模块,或通过动态模块加载)。njs 主要用于实现两类核心能力:

  • Location 处理器:接管特定路径的请求处理(如返回自定义响应、调用外部接口);
  • 变量处理器:自定义 NGINX 变量,动态生成变量值。

2. 最简示例:快速上手

以下是 njs 基础配置与脚本示例,覆盖核心用法(适配 njs 0.4.0+ 版本):

步骤 1:NGINX 主配置(nginx.conf)
nginx 复制代码
http {
    # 1. 导入 njs 脚本文件(核心指令:js_import)
    js_import http.js;

    # 2. 自定义 NGINX 变量(js_set 绑定 njs 函数)
    js_set $foo     http.foo;
    js_set $summary http.summary;

    server {
        listen 8000;
        charset utf-8;

        # 3. 自定义 content 处理器(js_content 绑定 njs 函数)
        location / {
            add_header X-Foo $foo; # 使用自定义变量
            js_content http.baz;    # 接管响应内容
        }

        # 直接返回自定义变量值
        location = /summary {
            return 200 $summary;
        }

        # 简单的文本响应
        location = /hello {
            js_content http.hello;
        }
    }
}
步骤 2:njs 脚本文件(http.js)
javascript 复制代码
// 1. 自定义变量处理器:返回固定值
function foo(r) {
    r.log("执行 foo 变量处理器"); // 写入 NGINX 日志
    return "foo-value";
}

// 2. 自定义变量处理器:生成请求摘要信息
function summary(r) {
    let s = "请求摘要信息\n\n";
    // 读取请求对象(r)的核心属性
    s += "请求方法:" + r.method + "\n";
    s += "HTTP 版本:" + r.httpVersion + "\n";
    s += "请求主机:" + r.headersIn.host + "\n";
    s += "客户端IP:" + r.remoteAddress + "\n";
    s += "请求URI:" + r.uri + "\n";
    
    // 遍历请求头
    s += "请求头:\n";
    for (let h in r.headersIn) {
        s += `  ${h}: ${r.headersIn[h]}\n`;
    }
    
    // 遍历查询参数(r.args 自动解析 URL 查询字符串)
    s += "查询参数:\n";
    for (let a in r.args) {
        s += `  ${a}: ${r.args[a]}\n`;
    }
    return s;
}

// 3. 自定义 content 处理器:分步构建响应
function baz(r) {
    r.status = 200; // 设置响应状态码
    // 设置响应头
    r.headersOut['Content-Type'] = "text/plain; charset=utf-8";
    r.headersOut['Content-Length'] = 15;
    // 发送响应头
    r.sendHeader();
    // 分段发送响应体
    r.send("nginx");
    r.send("java");
    r.send("script");
    // 结束响应
    r.finish();
}

// 4. 简化的响应方式:直接返回状态码+内容
function hello(r) {
    r.return(200, "Hello njs!");
}

// 导出函数(供 NGINX 配置调用)
export default { foo, summary, baz, hello };
测试验证
  • 访问 http://localhost:8000/hello:返回 Hello njs!;
  • 访问 http://localhost:8000/summary?name=test&age=20:返回包含请求方法、头信息、查询参数的摘要文本;
  • 访问 http://localhost:8000/:返回 nginxjavascript(分段发送的内容拼接),响应头包含 X-Foo: foo-value。

二、njs 核心指令详解

njs 的能力通过一系列专属指令实现,以下是高频使用的核心指令分类说明:

1. 脚本导入与基础指令

指令 作用 适用版本
js_import 导入 njs 模块(支持命名空间),替代旧版 js_include 0.4.0+
js_content 将 njs 函数设为 location 内容处理器,接管请求响应 全版本
js_set 将 njs 函数绑定到 NGINX 变量,函数返回值作为变量值 全版本
js_path 设置 njs 模块的额外加载路径 0.3.0+
js_var 声明可写的 NGINX 变量(重定向后不重置) 0.5.3+
关键说明:
  • js_import 支持两种写法:

    nginx 复制代码
    js_import http.js; # 以文件名作为命名空间(http.函数名)
    js_import myHttp from http.js; # 自定义命名空间(myHttp.函数名)
  • js_include 已被废弃(0.7.1 移除),优先使用 js_import。

2. 响应过滤指令(修改响应内容/头)

指令 作用 适用版本
js_header_filter 设置响应头过滤器,修改/删除响应头(如清除 Content-Length) 0.5.1+
js_body_filter 设置响应体过滤器,逐块修改响应内容(如大小写转换、内容替换) 0.5.2+
实战示例:响应体小写转换
nginx 复制代码
# NGINX 配置
location /filter {
    proxy_pass http://localhost:8080;
    # 清除 Content-Length(避免长度不匹配)
    js_header_filter http.clearContentLength;
    # 响应体过滤器:转小写
    js_body_filter http.lowercaseFilter;
}
javascript 复制代码
// http.js 新增函数
function clearContentLength(r) {
    delete r.headersOut['Content-Length'];
}

function lowercaseFilter(r, data, flags) {
    // 逐块处理响应体,转小写后传递给下一个过滤器
    r.sendBuffer(data.toLowerCase(), flags);
}

3. Fetch API 相关指令(0.7.0+)

njs 0.7.0 新增 ngx.fetch 方法,支持发起 HTTP/HTTPS 请求,配套指令用于配置请求规则:

指令 作用
js_fetch_trusted_certificate 指定 HTTPS 证书验证的可信 CA 文件(PEM 格式)
js_fetch_verify 启用/禁用 HTTPS 证书验证(生产环境建议开启)
js_fetch_timeout 设置 Fetch 请求的读写超时时间(默认 60s)
js_fetch_proxy 配置 Fetch 请求的正向代理(支持 HTTP 代理,0.9.4+)
js_fetch_keepalive 启用 Fetch 连接池,设置最大空闲连接数(0.9.2+)
实战示例:异步调用外部接口
nginx 复制代码
# NGINX 配置
location = /fetch {
    js_content http.fetch;
    js_fetch_trusted_certificate /path/to/ISRG_Root_X1.pem;
    js_fetch_keepalive 32; # 连接池最大空闲连接数
}
javascript 复制代码
// http.js 新增异步函数
async function fetch(r) {
    try {
        // 并行调用两个接口
        let results = await Promise.all([
            ngx.fetch('https://nginx.org/'),
            ngx.fetch('https://nginx.org/en/')
        ]);
        // 解析响应并返回
        let response = [];
        for (let res of results) {
            response.push({
                status: res.status,
                body: await res.text()
            });
        }
        r.return(200, JSON.stringify(response, null, 2), {
            'Content-Type': 'application/json; charset=utf-8'
        });
    } catch (e) {
        r.return(500, `请求失败:${e.message}`);
    }
}

4. 高级特性指令

4.1 共享字典(0.8.0+):跨 worker 进程共享数据
nginx 复制代码
# NGINX 配置:创建 1MB 共享字典,60s 超时,支持淘汰旧数据
js_shared_dict_zone zone=myDict:1M timeout=60s evict;
javascript 复制代码
// http.js 操作共享字典
function setDict(r) {
    // 设置键值对
    let result = ngx.shared.myDict.set(r.args.key, r.args.value);
    r.return(200, `设置结果:${result}`);
}

function getDict(r) {
    // 获取键值对
    let value = ngx.shared.myDict.get(r.args.key);
    r.return(200, `获取结果:${value || '不存在'}`);
}
4.2 定时任务(0.8.1+):周期性执行函数
nginx 复制代码
# NGINX 配置:每分钟在所有 worker 进程执行定时任务
location @periodic {
    js_periodic http.periodicTask interval=60s worker_affinity=all;
    js_fetch_trusted_certificate /path/to/ISRG_Root_X1.pem;
}
javascript 复制代码
// http.js 定时任务函数
async function periodicTask(s) {
    try {
        // 定时拉取页面并写入日志
        let res = await ngx.fetch('https://nginx.org/en/docs/njs/');
        let body = await res.text();
        ngx.log(ngx.INFO, `定时任务执行:页面长度 ${body.length}`);
    } catch (e) {
        ngx.log(ngx.ERR, `定时任务失败:${e.message}`);
    }
}

三、njs 核心对象与常用 API

njs 脚本的核心是请求对象(r) 和全局对象(ngx),以下是高频使用的属性/方法:

1. 请求对象(r):处理请求的核心

属性/方法 作用
r.method 获取请求方法(GET/POST/PUT 等)
r.headersIn 请求头对象(如 r.headersIn.host 获取主机名)
r.headersOut 响应头对象(设置响应头,如 r.headersOut['Content-Type'] = 'text/plain')
r.args 自动解析的查询参数对象(只读,重复键为数组,自动解码)
r.variables NGINX 内置变量(如 r.variables.arg_foo 获取 foo 参数第一个值)
r.return(status, body, headers) 快速返回响应(状态码+内容+响应头)
r.sendHeader() 发送响应头(分步构建响应时使用)
r.send(data) 分段发送响应体
r.log(msg) 写入 NGINX 错误日志

2. 全局对象(ngx):高级能力入口

属性/方法 作用
ngx.fetch(url, opts) 发起 HTTP/HTTPS 请求(异步,返回 Promise)
ngx.shared.zoneName 访问共享字典(如 ngx.shared.myDict.get(key))
ngx.log(level, msg) 写入日志(level:ngx.INFO/ngx.ERR 等)
crypto.subtle.digest 加密哈希(如 SHA-512,0.7.0+)

四、njs 最佳实践与注意事项

1. 版本兼容性

  • 优先使用 njs 0.7.0+ 版本,支持异步 await、Fetch API、加密等高级特性;
  • 旧版 js_include 已废弃,升级为 js_import;
  • r.headersOut 直接赋值在低版本可能报错,可改用 r.header(key, value)。

2. 性能与安全

  • 同步/异步限制 :js_set/js_header_filter/js_body_filter 仅支持同步操作,异步需用 js_content + async/await;
  • 共享字典优化 :设置合理的 timeout 和 evict,避免内存溢出;
  • 证书验证 :Fetch API 调用 HTTPS 接口时,务必配置 js_fetch_trusted_certificate,开启 js_fetch_verify on;
  • 参数校验 :避免直接使用 r.args/r.headersIn 中的值,防止注入攻击。

3. 调试技巧

  • 开启 NGINX 调试日志:error_log /var/log/nginx/error.log debug;;
  • 使用 r.log()/ngx.log() 打印关键变量,定位问题;
  • 测试异步函数时,确保捕获所有异常,避免 NGINX 抛出 500 错误。

总结

njs 是 NGINX 从"配置驱动"到"配置+编程驱动"的重要扩展,通过 ngx_http_js_module 模块的丰富指令,可实现请求处理、响应过滤、异步调用、跨进程数据共享、定时任务等核心能力。掌握 njs 的核心指令(js_import/js_content/js_set)、请求对象(r)和全局对象(ngx),能大幅提升 NGINX 的灵活性,无需额外部署服务即可完成复杂的网关层逻辑。在实际使用中,需注意版本兼容性和性能优化,结合业务场景合理选择同步/异步方式,确保稳定与高效。

相关推荐
幸福清风4 小时前
从域名到一行 nginx 配置:一次站点 HTTPS 证书更换与排障复盘
python·nginx·https
梅梅绵绵冰6 小时前
Nginx反向代理
运维·nginx
2601_965619691 天前
AI视频制作:财经数据可视化视频怎么做:AI 自动生成图表动画的实操流程
人工智能·nginx·音视频
2601_965619691 天前
历史解说视频怎么做:青铜器瓷器文物画面自动对齐的完整教程
运维·nginx·音视频
姜鱼问生2 天前
每次开机手动检查 6 样东西?我写了个 2 秒自检脚本
运维·网络·nginx·docker·容器
xixingzhe23 天前
Nginx 看懂请求耗时瓶颈
服务器·前端·nginx
北风toto3 天前
Windows 下用 OpenSSL 生成 SSL 密钥并配置 Nginx 教程
windows·nginx·ssl
卜锦元3 天前
Docker Compose 部署 Nginx 1.30.6 完整指南
nginx·docker·容器
小小龙学IT3 天前
Nginx 开源 Web 服务器与反向代理深度解
前端·nginx·开源
Coisinier4 天前
docker命令管理镜像及容器
linux·运维·nginx·docker·容器·操作系统