Spring WebSocket连接失败:Upgrade Header为空问题求助
问题
我用Java Spring搭建了WebSocket服务,本地通过ws://localhost:8080/socket和ws://localhost:30019/socket连接均正常。部署到服务器后,通过ws://website.com/socket无法连接——服务器使用代理将80端口转发至服务所在的30019端口,静态页面访问正常。
连接时收到服务器400响应,触发WebSocketHandshakeException,异常栈如下:
Exception in thread "main" java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.net.http.WebSocketHandshakeException at com.socketsimpl.Main.main(Main.java:34) Caused by: java.util.concurrent.ExecutionException: java.net.http.WebSocketHandshakeException at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396) at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073) at com.socketsimpl.Main.main(Main.java:27) Caused by: java.net.http.WebSocketHandshakeException at java.net.http/jdk.internal.net.http.websocket.OpeningHandshake.resultFrom(OpeningHandshake.java:226) at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150) at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) at java.base/java.util.concurrent.CompletableFuture.postFire(CompletableFuture.java:614) at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:844) at java.base/java.util.concurrent.CompletableFuture$Completion.exec(CompletableFuture.java:483) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) Caused by: jdk.internal.net.http.websocket.CheckFailedException: Unexpected HTTP response status code 400 at java.net.http/jdk.internal.net.http.websocket.OpeningHandshake.checkFailed(OpeningHandshake.java:343) at java.net.http/jdk.internal.net.http.websocket.OpeningHandshake.handleResponse(OpeningHandshake.java:252) at java.net.http/jdk.internal.net.http.websocket.OpeningHandshake.resultFrom(OpeningHandshake.java:222) ... 10 more
服务器控制台报错:Handshake failed due to invalid Upgrade header: null,开启DEBUG日志(在application.properties中添加logging.level.root=DEBUG)后发现请求中无Upgrade请求头。
我尝试修改客户端连接代码,手动添加Connection和Upgrade请求头:
HttpClient client = HttpClient.newHttpClient(); socket = client.newWebSocketBuilder() .header("Connection", "Upgrade") .header("Upgrade", "WebSocket") .buildAsync( URI.create("ws://website.com/socket"), new Listener() ) .get();
但这两个是Java受限请求头,触发IllegalArgumentException,提示restricted header name: "Connection"。按照网上建议添加启动参数java -"Djdk.httpclient.allowRestrictedHeaders=Upgrade,Connection" -jar .\untitled-1.0-SNAPSHOT-all.jar后,服务器日志仍未检测到这两个请求头,错误依旧。添加其他请求头可正常显示,说明添加参数的方式有效。
请问如何修复握手问题,让客户端成功连接服务器?
解决方案
1. 修正代理配置,确保WebSocket请求头被转发
问题根源是代理未正确传递WebSocket握手必需的Upgrade和Connection请求头到后端服务。针对常用代理的配置调整如下:
- Nginx:在对应location块中添加WebSocket支持配置,确保请求头被保留并转发:
location /socket { proxy_pass http://localhost:30019/socket; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; }
- Apache:启用
mod_proxy和mod_proxy_wstunnel模块,添加类似配置:
ProxyPass /socket ws://localhost:30019/socket ProxyPassReverse /socket ws://localhost:30019/socket
- Caddy:直接在配置中指定转发规则,Caddy默认支持WebSocket:
website.com { reverse_proxy /socket localhost:30019 }
2. 恢复客户端默认代码,无需手动添加受限请求头
Java HttpClient在构建WebSocket连接时会自动生成并添加Upgrade和Connection请求头,手动添加反而会引发冲突。恢复客户端代码至默认状态:
HttpClient client = HttpClient.newHttpClient(); socket = client.newWebSocketBuilder() .buildAsync( URI.create("ws://website.com/socket"), new Listener() ) .get();
3. 验证代理转发的请求头完整性
用curl命令测试握手请求,确认代理是否正确传递了WebSocket相关请求头:
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" -H "Sec-WebSocket-Version: 13" http://website.com/socket
如果返回101 Switching Protocols,说明代理配置正确;若仍返回400,继续排查代理的请求头过滤规则。
4. 检查Spring WebSocket配置
确保Spring的WebSocket配置未拦截或过滤请求头:
- 若使用自定义
HandshakeInterceptor,检查其beforeHandshake方法是否正确保留了Upgrade和Connection请求头。 - 确认Spring版本与WebSocket规范兼容,无版本适配问题。
内容的提问来源于stack exchange,提问作者KingsDev

