非标端口引起的反代额外配置 http_host 问题
2026.11.05
研究背景 Background
自建管理面板(1Panel)部署在非标准端口上,前置 OpenResty 反向代理,另有一条 FRP 通道用于外网访问。面板的常规功能——文件管理、容器列表、应用商店——均正常,唯独网页终端持续返回 The connection has been disconnected。
服务端认证链路正常:SSH 客户端可直接登录,sshd 日志记录了成功认证。由此可排除账号、密码、网络连通性与目标端口问题,故障范围收敛到终端 WebSocket 的握手环节。
初次排查按常规思路检查 WebSocket 转发配置,包括 Upgrade、Connection 头与 http2 设置,但逐项调整后终端仍未恢复。后续通过浏览器 Network 面板确认 WebSocket 请求状态为 failed,而非 HTTP 层错误,才将排查方向转向请求头内容本身。
参考资料 References
- 1Panel v2.4.0 起为终端 WebSocket 增加同源校验(Origin / Host 校验)
- Nginx 变量语义:
$host仅保留主机名,$http_host保留请求原始Host头(含端口) - 既有同类记录:Nextcloud 及 OnlyOffice / Baikal 反代配置、Code Server 反代配置
- HTTP/2 协议已废弃
Upgrade机制,WebSocket 需通过 HTTP/1.1 升级
技术总结 Technical Summary
面板终端 WebSocket 连接失败的直接原因是反向代理改写 Host 头时使用了 $host,该变量丢弃端口,导致后端同源校验失败。将代理配置改为 $http_host 后握手恢复,WS 状态由 failed 变为 101 Switching Protocols。
Host 头在链路上的变化如下:
| 环节 | 实际收到的 Host |
|---|---|
| 浏览器请求 | panel.example.com:8369 |
Nginx $host |
panel.example.com |
| 面板后端 | panel.example.com(端口丢失) |
后端期望的 Host 与实际值不一致,握手被拒绝。此类问题在非标准端口场景下必然出现,与证书、SNI、防火墙无关。
细节 Details
一、现象与影响范围
故障表现为终端不可用,面板其余功能正常。同一面板通过域名反代、FRP、不同端口等多条路径访问时,终端在所有路径上同时失效。
这一特征是判断故障层级的首要依据:单条反向代理配置错误通常只影响对应路径;多条路径同时失效,说明拒绝发生在面板后端,而非某一条代理规则。
二、根因分析:Host 头端口丢失
反向代理的常见写法为:
1 | |
$host 取自请求行或 Host 头的主机名部分,不包含端口。当面板监听非标准端口时,浏览器发出的 Host 为 域名:端口,经代理改写后端口丢失,后端进行同源校验时比对失败。
FRP 场景同理:FRP 在 TCP 层转发,不修改 HTTP 头,Host 中的端口信息与后端预期同样可能不一致。
三、修复步骤
在所有反向代理该面板的 location 块中,将 Host 的取值改为 $http_host:
1 | |
修改后先校验配置,再重载服务:
1 | |
$http_host 使用浏览器发送的原始 Host 头,完整保留主机名与端口,同源校验通过。
四、验证标准
WebSocket 是否恢复,以浏览器 Network 面板的请求状态为准:
- 修复前:
wss://...请求状态failed - 修复后:状态
101 Switching Protocols
不建议使用curl判断。该终端的 WebSocket 需要先获取 ticket 再握手,直接发送Upgrade头会被后端按普通 HTTP 请求处理并返回200,该结果不能说明代理配置正确或错误。
五、排查易错点
1. **$connection_upgrade** 缺少 **map** 定义
配置中引用 $connection_upgrade,但 http 上下文中的 map 定义被删除或注释时,Nginx 启动报 unknown "connection_upgrade" variable,容器进入重启循环,80/443 端口不再监听。使用该变量前必须确认 map 存在;不确定时可直接写 "upgrade"。
**2. 同一 **location** 重复声明 ****Connection**
两条 proxy_set_header Connection 语句时,后者覆盖前者;若后者引用未定义变量,整个配置无法启动。一个 location 中只保留一条。
3. **http2 on** 与 WebSocket 冲突
启用 HTTP/2 后,浏览器可能通过 HTTP/2 发起 WebSocket,而 HTTP/2 不支持 Upgrade,握手会被静默丢弃。承载 WebSocket 的端口不应开启 http2,或将其与 443 拆分为两个 server 块。
4. 以宿主机目录判断配置是否生效
站点配置通过主配置 include 加载,容器内路径与宿主机路径不一致。确认最终生效配置应使用 docker exec <容器> openresty -T,该命令会展开所有 include 并标注文件来源。
5. 以 HTTP 状态码判断 WebSocket 结果
如第四节所述,curl 返回 200 不代表代理配置错误,也不能代表正确。判断依据只有浏览器中的 WS 状态码。
六、同类场景与适用范围
该问题不限于 1Panel。站内已有记录显示,以下服务在非标准端口 + 反向代理场景下存在同一根因:
- Nextcloud 及周边组件(OnlyOffice、Baikal):回调地址与部分 API 依赖
Host生成绝对地址,端口丢失后出现登录跳转异常或附件不可用 - Code Server:反向代理访问时 WebSocket 连接失败,原因同为
Host不完整 - Vaultwarden:开启 WebSocket 通知时,反代配置中的
Host取值会影响同步通知链路
共性条件为:服务监听非标准端口,且依赖 WebSocket 或根据Host生成绝对 URL。满足这两个条件时,$host即存在风险。
七、检查清单
- 反代面板类服务时,
Host使用$http_host,不使用$host - 承载 WebSocket 的端口不开启
http2 on - 修改配置前执行
openresty -t,通过后再reload,避免直接重启容器 - WebSocket 是否正常,以浏览器 WS 状态码
101为唯一判断依据 - 终端类故障优先确认多条访问路径是否同时失效,据此区分全局校验问题与单条代理配置问题
附:排查决策树
1 | |
留言
留言经审核后才会公开显示:审核通过后刷新本页即可看到。