Nginx Proxy Manager 端口转发(Stream)功能详解:TCP/UDP 流量转发实战
【免费下载链接】nginx-proxy-managerDocker container for managing Nginx proxy hosts with a simple, powerful interface项目地址: https://gitcode.com/GitHub_Trending/ng/nginx-proxy-manager
导读
本文围绕 Nginx Proxy Manager 的Stream(端口转发)功能展开。Stream 基于 Nginx 的流模块,能够将 TCP/UDP 流量直接转发到网络上的另一台计算机,适用于游戏服务器、FTP、SSH 等非 HTTP 协议的代理场景。读完本文,你将掌握端口转发的适用场景、配置字段含义、SSL 终止支持、底层 Nginx 配置生成原理,以及如何通过 API 或源码理解其完整工作链路。
什么是端口转发(Stream)?
在 Nginx Proxy Manager 中,Stream(端口转发)是一个相对较新的功能,它的核心作用是把 TCP/UDP 流量直接转发到网络中的另一台计算机。
与 HTTP/HTTPS 反向代理(Proxy Host)不同,Stream 不关心应用层协议,而是在**传输层(Layer 4)**按 IP 和端口转发原始数据流。因此,凡是基于 TCP 或 UDP 的协议,都可以通过 Stream 暴露到公网:
- 游戏服务器:如 Minecraft、CS 等需要固定端口直连的游戏服务;
- FTP 服务:传统 FTP 使用 21 端口控制连接,主动/被动模式下还需要转发数据端口;
- SSH 服务:将内网主机的 SSH 端口映射到公网,方便远程管理;
- 数据库连接:MySQL(3306)、PostgreSQL(5432)等;
- 其他自定义 TCP/UDP 服务。
如果你正在运行上述服务,端口转发功能就能派上用场。它的价值在于:你可以用一台部署了 Nginx Proxy Manager 的公网主机统一收口所有内网服务的入站流量,无需为每个服务单独购买公网 IP 或调整路由器端口映射。
端口转发与 HTTP 代理的区别
在 Nginx Proxy Manager 的管理界面中,Stream 与 Proxy Host(HTTP 代理)是两类独立的资源,理解它们的差异有助于选对功能:
| 维度 | Proxy Host(HTTP 代理) | Stream(端口转发) |
|---|---|---|
| 工作层级 | 应用层(HTTP/HTTPS) | 传输层(TCP/UDP) |
| 路由依据 | 域名 + Host 头 | IP + 端口 |
| 典型协议 | HTTP、HTTPS、WebSocket | SSH、FTP、游戏协议、数据库 |
| 是否支持 SSL | 支持,且通常是核心诉求 | 支持 TCP 上的 SSL(流式透传) |
| 域名配置 | 必须配置domain_names | 不按域名路由 |
这一区别在源码中也有明确体现:在 backend/internal/stream.js 的create流程里,有一段注释写道streams aren't routed by domain name so don't store domain names in the DB(Stream 不按域名路由,因此不在数据库中存储域名),并在入库前通过delete data_no_domains.domain_names把domain_names字段移除。这说明 Stream 从数据模型层面就与域名解耦,纯粹以端口为核心标识。
核心概念:入站端口与转发目标
一个 Stream 配置本质上只回答两个问题:
- 监听哪个入站端口(incoming_port);
- 把流量转发到哪里(forwarding_host + forwarding_port)。
在管理界面中,创建 Stream 时需要在Details(详细信息)标签页填写以下核心字段(对应前端弹窗 frontend/src/modals/StreamModal.tsx 中的表单):
- Incoming Port(入站端口):Nginx 监听的公网端口,取值范围 1~65535。前端通过
validateNumber(1, 65535)校验(见 frontend/src/modals/StreamModal.tsx),OpenAPI 规范同样约束为 1~65535(见 backend/schema/components/stream-object.json)。 - Forward Host / IP(转发目标主机):目标计算机的域名、IPv4 或 IPv6 地址。OpenAPI 规范允许域名(如
example.com)、IPv4(0.0.0.0~255.255.255.255格式)或 IPv6(见 backend/schema/components/stream-object.json)。 - Forward Port(转发端口):目标主机上服务的真实端口,同样取值 1~65535。
- TCP Forwarding(TCP 转发):布尔开关,是否转发 TCP 流量。
- UDP Forwarding(UDP 转发):布尔开关,是否转发 UDP 流量。
- SSL Certificate(SSL 证书):可选,用于对入站 TCP 连接启用 SSL 终止/透传(详见下文)。
- Enabled(启用):是否立即生效。
从数据库模型看,tcp_forwarding与udp_forwarding是独立的布尔字段(见 backend/models/stream.js),二者可以同时开启,也可以只开其一。若同时开启,Nginx 会为同一入站端口生成 TCP 与 UDP 两套server块(见下文模板分析)。
在管理界面中,Stream 列表默认按incoming_port升序排列(见 backend/internal/stream.js 与 backend/models/stream.js),方便运维人员快速定位端口占用情况。
生成的真实 Nginx 配置解析
Nginx Proxy Manager 的本质是「配置生成器」:你在界面上填写的表单,最终会被渲染成一段标准 Nginx 配置。Stream 的配置模板位于 backend/templates/stream.conf,其核心逻辑如下:
# ------------------------------------------------------------ # {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }} # ------------------------------------------------------------ {% if enabled %} {% if tcp_forwarding == 1 or tcp_forwarding == true -%} server { listen {{ incoming_port }} reuseport {%- if certificate %} ssl {%- endif %}; {% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} reuseport {%- if certificate %} ssl {%- endif %}; {%- include "_certificates_stream.conf" %} proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; access_log /data/logs/stream-{{ id }}_access.log stream; error_log /data/logs/stream-{{ id }}_error.log warn; # Custom include /data/nginx/custom/server_stream[.]conf; include /data/nginx/custom/server_stream_tcp[.]conf; } {% endif %} {% if udp_forwarding == 1 or udp_forwarding == true -%} server { listen {{ incoming_port }} udp reuseport; {% unless ipv6 -%} # {%- endunless -%} listen [::]:{{ incoming_port }} udp reuseport; proxy_pass {{ forwarding_host }}:{{ forwarding_port }}; access_log /data/logs/stream-{{ id }}_access.log stream; error_log /data/logs/stream-{{ id }}_error.log warn; # Custom include /data/nginx/custom/server_stream[.]conf; include /data/nginx/custom/server_stream_udp[.]conf; } {% endif %} {% endif %}这段模板揭示了几个关键实现细节:
条件渲染由
enabled开关控制:{% if enabled %}保证禁用(disabled)的 Stream 不会生成任何server块,这正是「启用/禁用」功能的底层实现原理——见 backend/internal/stream.js,enable会调用internalNginx.configure重新生成配置,而disable会调用internalNginx.deleteConfig删除配置并reload。TCP 与 UDP 是两套独立的
server块:TCP 块使用listen {{ incoming_port }} reuseport(可选追加ssl),UDP 块使用listen {{ incoming_port }} udp reuseport。reuseport指令允许多个 socket 绑定同一端口,提升多核场景下的分发性能。IPv6 双栈支持:每套
server块都会尝试监听[::]:{{ incoming_port }},当项目未启用 IPv6 时(ipv6为假),模板通过{% unless ipv6 -%} # {%- endunless -%}输出#注释掉该行,实现优雅降级。流式访问日志:每个 Stream 生成独立的访问/错误日志文件
/data/logs/stream-{{ id }}_access.log与stream-{{ id }}_error.log,方便按 Stream 维度排查流量。自定义配置钩子:模板末尾 include 了
/data/nginx/custom/server_stream[.]conf、server_stream_tcp[.]conf、server_stream_udp[.]conf三个可选文件(方括号.写法保证文件不存在时不报错),高级用户可以写入额外的 Nginx 指令。SSL 只在 TCP 块生效:
{%- if certificate %} ssl {%- endif %}表明当为该 Stream 绑定证书时,TCP 监听会启用ssl指令,证书相关配置由_certificates_stream.conf子模板注入(见 backend/templates/_certificates_stream.conf)。
SSL 支持:为 TCP 流量启用加密
虽然 Stream 面向的是非 HTTP 协议,但 Nginx Proxy Manager 仍允许为 TCP 转发绑定SSL 证书,实现「入站加密 → 内网明文」或「入站加密 → 内网同样加密」的透传模式。
配置方式与 Proxy Host 类似:在 Stream 弹窗的SSL 标签页(见 frontend/src/modals/StreamModal.tsx)中,通过SSLCertificateField选择一个已有证书,或选择「新建证书」快捷创建(certificate_id === "new")。
在 API 层,当提交certificate_id: "new"时,backend/internal/stream.js 会调用internalCertificate.createQuickCertificate(access, data)先快速签发证书,再回填certificate_id完成创建;更新(update)流程同样支持这一快捷路径(见 backend/internal/stream.js)。这表明 Stream 与证书模块是深度集成的,而不是简单的字段挂载。
需要说明的是:Stream 的 SSL 是流式透传/终止于 Nginx 层,与 HTTP 代理的证书续期、HSTS 等 Web 特性无关——因为 Nginx 的stream模块工作在传输层,_certificates_stream.conf只注入ssl_certificate、ssl_certificate_key等与 TCP 加密直接相关的指令。
Stream 的完整生命周期:从 API 到 Nginx 配置
理解一个 Stream 从创建到生效的完整链路,有助于在生产环境中排查问题。整个过程由 backend/internal/stream.js 驱动,分为以下步骤:
- 权限校验:调用
access.can("streams:create", data)检查当前用户是否具备创建权限。权限模型定义在 backend/lib/access/streams-create.json:管理员可直接操作,普通用户则需要permission_streams具备 manage 权限。 - 数据入库:通过
streamModel.query().insertAndFetch(data)写入stream表(backend/models/stream.js)。模型层会自动维护created_on/modified_on时间戳,并把enabled、tcp_forwarding、udp_forwarding等布尔字段与整数在数据库层面互相转换(见 backend/models/stream.js)。 - (可选)快速签发证书:若选择新建证书,会先调用证书模块,再把
certificate_id回填。 - 生成并应用 Nginx 配置:调用
internalNginx.configure(streamModel, "stream", row),用 backend/templates/stream.conf 渲染出实际配置并触发 Nginx reload。 - 写入审计日志:调用
internalAuditLog.add记录created/updated/deleted/enabled/disabled等动作(见 backend/internal/stream.js),所有操作均可追溯。
更新(update)流程同样遵循「权限校验 → 入库 → 重新生成配置 → 审计」的链路;而删除(delete)与禁用(disable)则调用internalNginx.deleteConfig("stream", row)移除配置文件并reload(见 backend/internal/stream.js)。从源码结构看,enable/disable被实现为独立操作而非字段编辑,便于在审计日志中区分操作类型。
列表查询(getAll)则按is_deleted = 0过滤,默认关联加载certificate与owner关系(见 backend/models/stream.js),并支持通过expand参数按需展开关联数据(见 backend/internal/stream.js),这也是前端 Stream 列表页展示证书与所有者信息的底层来源。
通过 REST API 管理 Stream
Nginx Proxy Manager 提供了完整的 REST API 来管理端口转发,OpenAPI 规范中的 Stream 对象定义在 backend/schema/components/stream-object.json,核心字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
id | integer | 主键 |
incoming_port | integer (1–65535) | 入站监听端口,必填 |
forwarding_host | string | 转发目标域名/IPv4/IPv6,必填 |
forwarding_port | integer (1–65535) | 转发目标端口,必填 |
tcp_forwarding | boolean | 是否启用 TCP 转发,必填 |
udp_forwarding | boolean | 是否启用 UDP 转发,必填 |
enabled | boolean | 是否启用,必填 |
certificate_id | integer | 可选,绑定的 SSL 证书 ID |
meta | object | 扩展元数据,默认为{} |
owner_user_id | integer | 归属用户,必填 |
对应的 API 端点包括(定义见 backend/schema/paths/nginx/streams 与 backend/routes/nginx/streams.js):
GET /api/nginx/streams—— 列出所有 Stream;POST /api/nginx/streams—— 新建 Stream;GET /api/nginx/streams/{streamID}—— 获取单个 Stream;PUT /api/nginx/streams/{streamID}—— 更新 Stream;DELETE /api/nginx/streams/{streamID}—— 删除 Stream;POST /api/nginx/streams/{streamID}/enable与POST /api/nginx/streams/{streamID}/disable—— 启用/禁用。
例如,创建一个「将公网 2222 端口转发到内网192.168.1.10的 22 端口(SSH)」的 Stream:
{ "incoming_port": 2222, "forwarding_host": "192.168.1.10", "forwarding_port": 22, "tcp_forwarding": true, "udp_forwarding": false, "enabled": true }提交后,后端会按照上文的生命周期流程生成配置并立即生效。前端通过 frontend/src/api/backend/createStream.ts、updateStream.ts 等封装调用这些端点,管理界面与 API 完全等价。
端口冲突与运维注意事项
从源码实现看,Stream 功能在端口管理上有一个值得注意的点:在 backend/internal/stream.js 的create与 backend/internal/stream.js 的update中,均存在TODO: At this point the existing ports should have been checked(此处应检查已有端口占用)的注释。也就是说,当前版本在后端并未显式校验入站端口是否已被其他 Stream 或系统服务占用,依赖 Nginx 配置 reload 阶段自行报错。
因此在生产环境中,建议运维人员注意:
- 入站端口需全局唯一:同一个
incoming_port不要重复用于多个 Stream,否则生成的 Nginx 配置会产生listen冲突,reload 时报错导致配置不生效。 - 避开宿主已占用端口:入站端口会绑定在运行 Nginx Proxy Manager 的主机上,请勿与 SSH(22)、Web 管理端口(81)等系统端口冲突。
- 关注防火墙/安全组:Stream 监听的是原始 TCP/UDP 端口,需在云厂商安全组与系统防火墙中放行对应入站流量,这与 HTTP 代理走 80/443 的情形不同。
- TCP/UDP 同开时注意协议差异:同一端口同时开启 TCP 与 UDP 转发是允许的(会生成两套
server块),但目标服务必须分别监听对应协议,否则会有一侧连接失败。 - 通过自定义配置钩子做精细控制:如需要额外指令(如
proxy_timeout、proxy_buffer_size等),可挂载到/data/nginx/custom/server_stream_tcp[.]conf或server_stream_udp[.]conf,避免直接改动生成的配置。
从源码进一步学习
如果你想深入了解 Stream 的实现细节,可以从以下文件入手(均在当前仓库内):
- backend/templates/stream.conf —— 端口转发最终的 Nginx 配置模板,是理解全部行为的最佳起点;
- backend/internal/stream.js —— Stream 的完整 CRUD、启停与审计逻辑;
- backend/models/stream.js —— 数据模型、布尔字段转换与关联关系(owner / certificate);
- backend/schema/components/stream-object.json —— OpenAPI 字段定义与取值范围;
- backend/routes/nginx/streams.js —— REST API 路由层;
- frontend/src/modals/StreamModal.tsx —— 前端创建/编辑表单与校验规则;
- frontend/src/pages/Nginx/Streams —— 前端列表页实现;
- backend/schema/paths/nginx/streams —— API 路径规范(含 enable/disable 等操作)。
总结
端口转发(Stream)是 Nginx Proxy Manager 中面向非 HTTP 流量的核心能力:它以入站端口 + 转发目标为模型,利用 Nginxstream模块在传输层完成 TCP/UDP 数据的透明转发,天然适用于 SSH、FTP、游戏服务器等场景。在实现上,Stream 与证书模块深度集成支持 TCP SSL,配置通过 backend/templates/stream.conf 模板渲染生成,具备 IPv6 双栈、独立日志、自定义配置钩子与完整审计能力;同时提供了与界面等价的 REST API,便于自动化管理。使用时只需注意入站端口的唯一性与防火墙放行,即可快速将内网任意 TCP/UDP 服务安全地暴露到公网。
【免费下载链接】nginx-proxy-managerDocker container for managing Nginx proxy hosts with a simple, powerful interface项目地址: https://gitcode.com/GitHub_Trending/ng/nginx-proxy-manager
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考