WebSocket 经 Caddy 从 ws 换成 wss 后失败:检查 Spring 允许的 Origin
HTTP WebSocket 正常、HTTPS 页面连接失败时,核对握手的 Origin 与 Spring 允许来源。区分后端 403、TLS 和代理连接故障,保留认证及消息权限。
Spring WebSocket 应用直连 HTTP 正常,经 Caddy 的 HTTP 入口也正常,改用 HTTPS 页面后,控制台却提示 WebSocket connection failed。若失败握手返回 403,后端已收到升级请求,应检查应用是否只允许 HTTP 页面来源。
页面变为 https://app.example.com 后,浏览器握手中的 Origin 也会变为这个 HTTPS 来源。WebSocket 地址使用 wss://,不意味着 Origin 应写成 wss://。Origin 表示发起连接的页面来源,包含协议、主机及适用的端口。
本文针对 Caddy 2 反向代理到 Spring WebSocket/STOMP 服务的浏览器连接。Caddy 支持 WebSocket 升级;应用拒绝来源与代理不支持升级,应根据握手响应分别判断。
先看握手在哪一层失败
在浏览器 Network 中找到对应 WebSocket 请求,核对完整 URL、Origin、状态码和响应内容,再对照同时间的应用握手日志。不要把包含 Cookie、令牌或查询参数的完整请求公开贴出。
如果连 TLS 都没有建立,浏览器没有得到 HTTP 状态,应先检查证书、端口与访问入口。若是 502,检查后端连接;如果是后端返回的 403,并且日志说明来源不被接受,再沿 Origin 规则处理。单独一行控制台错误不足以证明是哪一层。
有条件在隔离环境比较握手时,应保持地址、路径、授权和升级字段相同,只改变 HTTP 与 HTTPS 页面对应的 Origin。某一种来源成功、另一种被拒绝,才能把差异收窄到来源规则。不要通过改掉浏览器发送的 Origin 来掩盖应用配置。
在 Spring 端明确允许实际 HTTPS 页面
Spring Framework 从 4.1.5 起,WebSocket 和 SockJS 默认只接受同源请求。项目还可能显式配置允许来源;已有 HTTP 来源列表不会因为前置代理启用了 TLS,就自动增加 HTTPS 来源。
对于 STOMP 端点,可在既有 WebSocket 配置中调整端点注册。以下是方法片段示例,不是完整 Java 文件:
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket")
.setAllowedOrigins("https://app.example.com");
}
替换为实际页面来源和现有端点,保留项目需要的消息代理、拦截器、SockJS 及安全配置,不要再注册一份重复端点。页面使用非默认 HTTPS 端口时,应核对来源中的端口;来源值不是带 /gs-guide-websocket 的完整连接 URL。
若项目已调用 setAllowedOriginPatterns(Spring Framework 5.3.2 起提供),该模式配置优先于 setAllowedOrigins。应核对并修改实际生效的规则;保留旧模式、只添加具体来源,可能仍得不到预期结果。
应用有多个获准前端时,列出确实需要的来源,别为了通过一次测试直接允许 *。Origin 检查主要保护浏览器访问,非浏览器客户端能够自行构造这个头,因此仍需保留连接认证与消息级授权。
如果已允许正确来源却仍 403,应继续检查 Spring Security、握手拦截器、认证和项目的代理感知设置。setAllowedOrigins 不会覆盖所有安全规则,也不能代替 TLS 配置。
Caddy 通常不需要重写 Origin
例如服务确实与 Caddy 位于同一主机、在 127.0.0.1:8080 提供该端点时,站点结构可以是:
app.example.com {
reverse_proxy 127.0.0.1:8080
}
这是地址关系示例。分开部署的容器不能把 127.0.0.1 当成另一容器;应保留实际可达的上游地址和原来的 TLS 配置。
在这类传统握手路径中,Caddy 的 reverse_proxy 会处理升级并建立双向通道。不要照搬另一种代理的逐跳头配置,或强制把 HTTPS 页面发出的 Origin 改回 HTTP。后者可能使表面连接成功,却让后端按照错误的来源判断访问。
改动后验证连接和消息权限
修改前保存应用与代理配置。在隔离环境重新部署应用,必要时按现有发布方式滚动替换;配置更改可能中断既有连接,应安排客户端重连。只改变应用来源列表时,无须为此反复重载 Caddy。
传统 HTTP/1.1 WebSocket 的成功握手应返回 101 Switching Protocols,随后 STOMP 连接和消息交换也应正常。还要确认未获准的来源、错误凭据和无权限的消息操作仍被拒绝。页面 200 或客户端退回轮询,都不能单独证明 WebSocket 恢复。
回退时恢复保存的应用配置并重新部署,核对允许来源和认证规则。已经完成的消息处理不会因配置回退撤销;恢复期间出现连接中断,应按应用的重连和消息去重规则处理。


