Nginx + njs 是省资源又灵活的轻量HTTP逻辑处理方案,适用于鉴权前置、请求改写、灰度路由等场景;njs因官方内置、沙箱安全、语法亲和、上下文丰富而优于Lua和rewrite指令。

用 Nginx + njs 处理轻量 HTTP 逻辑,是比写后端服务更省资源、比纯配置更灵活的折中方案。它适合鉴权前置、请求改写、灰度路由、简单聚合等场景,不碰复杂业务,但能挡掉大量无效流量或标准化入口行为。
为什么选 njs 而不是 Lua 或 rewrite 指令
njs 是 Nginx 官方维护的嵌入式 JavaScript 引擎,轻量、安全、与配置深度集成。相比 OpenResty 的 Lua:
- njs 默认启用,无需额外编译或模块安装(Nginx 1.19.5+ 内置)
- 运行在受限沙箱中,不能访问文件系统、网络或执行系统命令,天然防误操作
- 语法接近标准 JS(ES5 主体 + 部分 ES6),前端同学上手快,调试也方便
- rewrite 指令只能做静态字符串替换,无法读取请求头、判断参数、调用时间函数等;njs 可以读取 $r.headersIn、$r.args、$r.remoteAddress 等上下文变量
一个真实可用的鉴权透传示例
比如要求所有 /api/ 路径必须带有效的 X-Api-Key,且只允许内网调用,否则返回 403:
# nginx.conf
js_import conf.d/auth.js;
server {
location /api/ {
js_filter auth.checkApiKey;
proxy_pass http://backend;
}
}对应 conf.d/auth.js:
function checkApiKey(r) {
const key = r.headersIn['X-Api-Key'];
const ip = r.remoteAddress;
<pre class="brush:php;toolbar:false;">if (!key || key !== 'prod-secret-2024') {
r.return(403, 'Invalid or missing API key\n');
return;
}
if (!ip.startsWith('10.') && !ip.startsWith('192.168.')) {
r.return(403, 'API access restricted to internal network\n');
return;
}
// 继续转发
r.internalRedirect('/proxy' + r.uri);}
export default { checkApiKey };
注意:js_filter 在请求进入 location 前执行,可中断流程;js_content 用于完全由 JS 生成响应;js_set 适合变量赋值(如生成 trace-id)。
常见陷阱与最佳实践
njs 看似简单,但几个细节容易踩坑:
- 变量作用域是函数级,不要在顶层声明状态变量(如计数器),每次请求都是全新上下文
- 异步操作(如 fetch)目前仅支持 experimental 版本,生产环境建议避免;同步逻辑务必控制在毫秒级,否则阻塞 worker 进程
- 日志用 r.warn() 或 r.error(),别用 console.log —— 它不会输出到 error_log
- 修改请求头要用 r.headersIn 读,但写入上游要用 r.headersOut 或 proxy_set_header 配合;njs 本身不能直接改写发往 upstream 的 header
- 脚本变更后需 reload Nginx(nginx -s reload),njs 不支持热重载
什么时候不该用 njs
以下情况建议交给真正后端处理:
- 需要连接数据库、Redis 或调用第三方 HTTP 接口(njs fetch 尚不稳定,且无连接池)
- 逻辑涉及复杂状态管理(如 session、长连接保活、流式响应)
- 需要完整单元测试、CI/CD、错误追踪体系
- 团队无 JS 维护能力,而运维只熟悉传统 Nginx 配置
把 njs 当作 Nginx 的“胶水层”,不是替代后端的方案。用对地方,它能让网关更干净、更可控、更轻快。

















