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
  • 共享字典优化 :设置合理的 timeoutevict,避免内存溢出;
  • 证书验证 :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 的灵活性,无需额外部署服务即可完成复杂的网关层逻辑。在实际使用中,需注意版本兼容性和性能优化,结合业务场景合理选择同步/异步方式,确保稳定与高效。

相关推荐
报错小能手3 小时前
nginx集群聊天室(一) 初步讲解集群聊天室所需库的搭建
c++·nginx
ICT董老师4 小时前
通过kubernetes部署nginx + php网站环境
运维·nginx·云原生·容器·kubernetes·php
bleach-5 小时前
buuctf系列解题思路祥讲--[SUCTF 2019]CheckIn1--文件上传以及user.ini的应用
nginx·web安全·网络安全·php
CodeCaptain5 小时前
配置Nginx反向代理来实现负载均衡,续阿里云ECS配置Nginx反向代理
nginx·阿里云·负载均衡
r***01386 小时前
Nginx代理到https地址忽略证书验证配置
运维·nginx·https
CodeCaptain6 小时前
阿里云ECS上配置Nginx的反向代理
nginx·阿里云·云计算
GDAL1 天前
NJS 共享字典(ngx.shared)全解析:跨 Worker 进程的数据共享方案
nginx·shared·njs
xifangge20251 天前
PHP 错误日志在哪里看?Apache / Nginx / PHP-FPM 一次讲清
nginx·php·apache
鸠摩智首席音效师1 天前
如何安装和配置 Nginx 反向代理服务器 ?
运维·nginx