不能通过 js_import(njs)在 Nginx 的 stream 模块中实现自定义 TCP 报文处理,因 njs 仅支持 http 和 mail 模块,stream 模块不提供报文级编程能力,官方明确禁止 js_import 在 stream 块中使用。

不能通过 js_import(njs)在 Nginx 的 stream 模块中实现自定义 TCP 报文处理。
原因:njs 不支持 stream 模块的报文级处理
Nginx 的 njs(JavaScript 模块)目前仅支持 http 和 mail 模块,不支持 stream 模块的 TCP/UDP 流量处理逻辑。官方文档明确说明:js_import 在 stream { ... } 块中是无效的,解析时会报错(如 "js_import" directive is not allowed here)。
stream 模块设计为轻量、零拷贝的四层代理,其配置阶段只允许极有限的变量扩展(如 $remote_addr),不提供用户可编程的报文读写、解析或修改能力。
当前 stream 模块支持的“自定义”方式(非常有限)
虽然无法用 JS 处理原始报文,但可通过以下机制做简单路由或元数据决策:
-
基于连接信息的条件转发:使用
map指令映射$remote_addr、$server_port或 TLS SNI(需ssl_preread on)到后端 upstream -
SSL/TLS 握手前元数据提取:启用
ssl_preread on后,可用$ssl_preread_server_name做 SNI 路由 -
基于 IP/端口的访问控制:用
allow/deny或geo+map控制连接建立
这些操作都在连接建立初期完成,不触及应用层 TCP payload。
若真需处理 TCP 报文内容,替代方案如下
必须脱离 Nginx stream 的限制,采用更底层或专用工具:
- 编写 Nginx C 模块:直接 hook stream upstream/downstream 的 read/write 钩子,解析/修改 TCP 流。开发门槛高,需熟悉 Nginx event loop 和内存管理
- 使用 eBPF/XDP(Linux 内核态):在网卡驱动或内核协议栈拦截 TCP 包,适合高性能报文过滤或监控,但无法修改 payload 并透明转发
- 代理到用户态 TCP 服务:Nginx stream 将连接反向代理到本地运行的 Node.js/Python/Rust 服务,由该服务完成完整 TCP 报文解析、业务逻辑、重封装与转发(典型如 HAProxy + Lua / Envoy + WASM)
- 改用支持 L4-L7 可编程的代理:如 Envoy(支持 WASM Filter 处理原始 TCP 字节流)、Traefik(TCP routing + plugin 扩展)、或自研基于 libevent/libuv 的代理
验证 njs 在 stream 中不可用的最小示例
以下配置会启动失败:
stream {
js_import tcp_handler.js; # ❌ 错误:directive not allowed here
js_set $backend tcp_handler.choose_backend;
<pre class='brush:php;toolbar:false;'>server {
listen 8080;
proxy_pass $backend;
}}
正确做法是:把 JS 逻辑移到 HTTP 模块(如用 js_content 提供配置 API),再由外部系统调用该 API 动态更新 stream 的 upstream 配置并 reload Nginx —— 这属于间接控制,非实时报文处理。


















