Laravel 10 搭配Soketi在VPS上遇WebSocket连接建立前关闭问题
我在Ubuntu 20.04的VPS上部署Laravel 10项目,用Soketi实现WebSocket,卡了好几天。目前Soketi容器正常运行,访问http://127.0.0.1:6001和http://xx.xxx.xx.xxx:6001都返回OK,但前端一直报WebSocket连接失败的错误。
已配置信息
Soketi容器配置
soketi: image: 'quay.io/soketi/soketi:latest-16-alpine' environment: SOKETI_METRICS_ENABLED: 1 ports: - '6001:6001' - '9601:9601' networks: - network_forms restart: always
.env配置
PUSHER_APP_ID=application id PUSHER_APP_KEY=application key PUSHER_APP_SECRET=application secret PUSHER_HOST=localhost PUSHER_PORT=6001 PUSHER_SCHEME=http PUSHER_APP_CLUSTER=mt1
config/broadcasting.php配置
'pusher' => [ 'driver' => 'pusher', 'key' => env('PUSHER_APP_KEY'), 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', 'port' => env('PUSHER_PORT', 443), 'schema' => env('PUSHER_SCHEME', 'https'), 'encrypted' => true, 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', ], 'customer_options' => [ // Guzzle client options ], ],
bootstrap.js配置
import echo from 'laravel-echo'; import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new echo({ broadcaster: 'pusher', key: import.meta.env.VITE_PUSHER_APP_KEY, cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1', wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`, wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80, wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443, forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https', encrypted: true, disableStats: true, enabledTransports: ['ws', 'wss'], });
已配置反向代理
ProxyPass/http://localhost:6001/ ProxyPassReverse / http://localhost:6001/
错误信息
WebSocket connection to 'wss://localhost:6001/app/app-key?protocol=7&client=js&version=7.6.0&flash=false' failed: WebSocket is closed before the connection is established.
修正前端WebSocket主机配置:前端代码里
wsHost用了localhost,会导致浏览器从客户端环境访问本地6001端口,而非VPS的公网IP/域名。需在.env中添加VITE前缀的环境变量:VITE_PUSHER_HOST=xx.xxx.xx.xxx # 你的VPS公网IP或域名 VITE_PUSHER_PORT=6001 VITE_PUSHER_SCHEME=http同时调整
bootstrap.js的forceTLS为false(当前用http协议),避免强制用wss连接http端口。修复反向代理配置:当前代理格式错误,正确的Apache配置需启用WebSocket代理模块并调整规则:
- 执行
a2enmod proxy_wstunnel启用模块,重启Apache - 修改代理规则:
ProxyPass /ws http://localhost:6001 ProxyPassReverse /ws http://localhost:6001 ProxyPass /api http://localhost:6001 ProxyPassReverse /api http://localhost:6001前端可通过
wss://你的域名/ws连接,既避免暴露端口,也解决跨域问题。- 执行
添加Soketi跨域允许:在Soketi容器环境变量中配置允许的前端域名,生产环境建议指定具体域名而非通配符:
environment: SOKETI_METRICS_ENABLED: 1 SOKETI_ALLOWED_ORIGINS: "https://yourdomain.com" # 替换为你的前端域名修正后端加密配置:
.env中PUSHER_SCHEME为http,但broadcasting.php里encrypted设为true,会导致后端用https连接Soketi的http端口。修改为:'encrypted' => env('PUSHER_SCHEME', 'https') === 'https',验证端口连通性:确保VPS防火墙和云服务商安全组放行6001端口,执行
ufw allow 6001/tcp开放端口。
内容的提问来源于stack exchange,提问作者Lucas Nascimento

