一、模块简介
ngx_http_js_module
(njs)将 JavaScript(子集)内嵌到 NGINX 配置中,可用于:
- 变量赋值:
js_set
、js_var
- 内容生成:
js_content
- 头部/主体过滤:
js_header_filter
、js_body_filter
- Fetch 调用:内置异步 HTTP 客户端
- 定时任务:
js_periodic
- 共享内存字典:
js_shared_dict_zone
- Crypto:浏览器级别加密操作
它支持 njs 自身引擎,也可切换至 QuickJS(js_engine qjs
),并允许灵活配置 SSL、超时、协议等 Fetch 细节。
二、安装与启用
-
获取 NGINX 商业版 或自行编译带
--with-http_js_module
。 -
njs 包:若自编译,需
--add-dynamic-module=path/to/nginx-module-njs
。 -
验证加载:
nginx -V 2>&1 | grep js_module # 应包含 --with-http_js_module
三、基础示例:动态变量与内容
3.1 导入与变量
http {
js_import http.js; # 导入模块
js_set $foo http.foo; # 设置变量 $foo
js_set $hash http.hash; # 设置 $hash
server {
listen 8000;
location /var {
return 200 $foo; # 输出 foo()
}
location /crypto {
add_header X-Hash $hash; # 在头部输出 hash()
return 200;
}
}
}
3.2 http.js
内容
// http.js
export default {
foo: function(r) {
return "Hello from njs!";
},
hash: async function(r) {
let digest = await crypto.subtle.digest("SHA-256", r.uri);
return Buffer.from(digest).toString("hex");
}
};
四、响应生成:js_content
在 location
中用 JavaScript 完全接管响应体:
location = /hello {
js_content http.hello;
}
// http.js
export function hello(r) {
r.return(200, `Hello, ${r.args.name||"world"}!`);
}
五、动态 Fetch:服务器间调用
location = /fetch {
js_content http.fetch;
js_fetch_trusted_certificate /etc/ssl/certs/ca.pem;
js_fetch_timeout 5s;
}
// http.js
export async function fetch(r) {
let res = await Promise.all([
ngx.fetch("https://api1/"),
ngx.fetch("https://api2/")
]);
let bodies = await Promise.all(res.map(r => r.text()));
r.return(200, bodies.join("\n---\n"));
}
六、过滤器:修改头部与主体
6.1 清除 Content-Length
location /proxy {
proxy_pass http://backend;
js_header_filter main.clearContentLength;
js_body_filter main.toUpper; # 示例:主体转大写
}
// main.js
export function clearContentLength(r) {
delete r.headersOut["Content-Length"];
}
export function toUpper(r, data, flags) {
r.sendBuffer(data.toString().toUpperCase(), flags);
}
注意:修改长度后务必清除
Content-Length
,否则客户端会等待错误长度。
七、定时任务:js_periodic
适合健康检查、数据预热、日志采集等后台任务:
location @tasks {
js_periodic tasks.refreshCache interval=60s jitter=5000 worker_affinity=0101;
}
// tasks.js
export async function refreshCache(s) {
let data = await ngx.fetch("https://config.service/latest");
let json = await data.json();
ngx.shared.config.set("latest", JSON.stringify(json), 3600);
}
八、共享字典:js_shared_dict_zone
在多个 worker 之间共享状态或缓存:
http {
js_shared_dict_zone zone=config:10m timeout=180s evict type=string;
js_import tasks.js;
server {
location = /config {
js_content tasks.getConfig;
}
location = /update {
js_content tasks.setConfig;
}
}
}
// tasks.js
export function getConfig(r) {
let val = ngx.shared.config.get("latest") || "{}";
r.return(200, val);
}
export function setConfig(r) {
ngx.shared.config.set("latest", r.args.value, 3600);
r.return(200, "OK");
}
九、性能调优与并发
- 上下文复用:
js_context_reuse 256
,减少 JS 上下文创建销毁开销 - 引擎选择:
js_engine qjs
(QuickJS 性能更高) - Fetch 缓冲区:根据响应大小调整
js_fetch_max_response_buffer_size
- SSL 参数:
js_fetch_ciphers
、js_fetch_protocols
、js_fetch_verify_depth
精细控制
十、最佳实践与安全
- 避免阻塞:过滤器仅支持同步操作;长耗时逻辑请使用
js_periodic
或后端服务。 - 严格验证:Fetch 默认验证证书,确保
js_fetch_trusted_certificate
指向可信根。 - 限流保护:配合
limit_req
、limit_conn
保护 JS 路由不被滥用。 - 错误捕获:在 JS 中用
try/catch
捕获异常,并在 NGINX 端统一返回 500 或日志记录。
通过以上功能,你可以让 NGINX 不仅是高效的反向代理和静态分发,还能变身轻量级应用服务器,直接用 JavaScript 实现路由、加工、调用与缓存逻辑,真正达成“配置即代码”的极简运维体验。