✨ 瞬间 15 📚 归档 11 🏷️ 标签 26
📂 分类
🏷️ 标签 (26)

非标端口引起的反代额外配置 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
proxy_set_header Host $host;

$host 取自请求行或 Host 头的主机名部分,不包含端口。当面板监听非标准端口时,浏览器发出的 Host 为 域名:端口,经代理改写后端口丢失,后端进行同源校验时比对失败。

FRP 场景同理:FRP 在 TCP 层转发,不修改 HTTP 头,Host 中的端口信息与后端预期同样可能不一致。

三、修复步骤

在所有反向代理该面板的 location 块中,将 Host 的取值改为 $http_host:

1
2
3
4
5
# 修改前
proxy_set_header Host $host;

# 修改后
proxy_set_header Host $http_host;

修改后先校验配置,再重载服务:

1
2
docker exec <openresty容器名> openresty -t
docker exec <openresty容器名> openresty -s reload

$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 即存在风险。

七、检查清单

  1. 反代面板类服务时,Host 使用 $http_host,不使用 $host
  2. 承载 WebSocket 的端口不开启 http2 on
  3. 修改配置前执行 openresty -t,通过后再 reload,避免直接重启容器
  4. WebSocket 是否正常,以浏览器 WS 状态码 101 为唯一判断依据
  5. 终端类故障优先确认多条访问路径是否同时失效,据此区分全局校验问题与单条代理配置问题

附:排查决策树

1
2
3
4
5
6
7
8
9
10
11
12
13
终端 disconnected
│
├─ 其他功能正常?
│ ├─ 否 → 检查面板服务、端口、防火墙
│ └─ 是 ↓
├─ SSH 日志成功?
│ ├─ 否 → 检查面板本地终端认证
│ └─ 是 ↓
├─ 多条访问路径同时失效?
│ ├─ 是 → 修改 Host $http_host,重载服务
│ └─ 否 → 检查该路径的 location 顺序 / http2 / WAF
│
└─ 修改后仍失败 → 查看面板后端日志中的 WebSocket 报错

留言

0/500

留言经审核后才会公开显示:审核通过后刷新本页即可看到。