nuxt 3 中 nitro 默认不支持 websocket 服务,客户端直连 ws://localhost:3000/ws 会失败;需独立启动 ws 服务或通过反向代理(如 nginx)将 upgrade 请求透传至 nitro 的 definewebsockethandler 处理。

在 Nuxt 3 中,WebSocket 不能直接在服务端(Nitro)启动一个对外暴露的 WebSocket 服务器,除非你手动接管底层 HTTP server;客户端连接也**不能直接用 ws:// 地址直连 Nitro 的 dev server 或生产 server**——因为默认的 Nitro server 不处理 WebSocket 升级请求。必须明确区分“谁提供 WS 服务”和“谁消费 WS 连接”。
为什么 useWebSocket 或 new WebSocket() 在客户端报错 Connection refused?
常见现象是控制台抛出 Failed to construct 'WebSocket': The URL 'ws://localhost:3000/ws' is invalid 或连接后立刻 onclose。根本原因是:
- Nitro 默认不监听或响应
Upgrade: websocket请求,即使你在nuxt.config.ts里配了@nuxtjs/proxy,它只代理 HTTP 请求,不代理 WebSocket 升级流量 -
ws://localhost:3000/xxx想让 Nitro 自己当 WS 服务端?不行——Nitro 不是ws库的封装,它没有内置 WebSocket server 实现 - 开发时浏览器访问
http://localhost:3000,但ws://localhost:3000/ws是另一个协议、另一层服务,必须有独立进程监听该端口并处理升级
所以,ws://localhost:3000/... 这类地址只在你**额外启动了一个 WebSocket 服务(如 ws、socket.io、Spring Boot WS endpoint)并确保端口可达**时才有效。
如何让客户端在 Nuxt 3 页面中安全建立 WebSocket 连接?
客户端代码本身没问题,关键在连接目标和生命周期管理:
- 连接地址必须指向真实运行的 WebSocket 服务,例如
ws://localhost:8080/ws(后端 Node.jsws.Server),或wss://api.example.com/chat(反向代理后的生产地址) - 不要在
setup()或onMounted里无条件新建WebSocket实例——SSR 渲染时会报错,需用process.client && ...包裹 - 务必手动清理:组件卸载时调用
socket.close(),否则可能残留连接、触发内存泄漏 - 若用
socket.io-client,注意它默认启用自动重连,但需配合服务端正确配置transports: ['websocket']避免降级到轮询
示例片段:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
onMounted(() => {
if (!process.client) return
const socket = new WebSocket('wss://your-ws-api.com/realtime')
socket.onopen = () => console.log('connected')
socket.onmessage = (e) => console.log(JSON.parse(e.data))
onUnmounted(() => {
socket.close()
})
})
如何在 Nuxt 3 Nitro 服务端接收并处理 WebSocket 连接?
Nitro 从 v2.8+ 开始支持原生 WebSocket 路由,但仅限于 server/routes/*.ts 下定义的 defineWebSocketHandler,且只能处理已由反向代理(如 Nginx、Cloudflare、Caddy)或 Node.js 原生 server 升级后的连接,Nitro 自身不监听 TCP 端口。
- 文件路径必须为
server/routes/ws.ts(后缀必须是.ts或.js),内容使用defineWebSocketHandler - 该 handler 不是“启动一个 WS 服务”,而是“处理已被升级的 WebSocket 请求的业务逻辑”,类似中间件
- 必须搭配外部 server:比如用
node:http启一个 server,把 upgrade 请求转发给 Nitro 的handler;或者部署时用 Nginx 把/ws路径的 upgrade 请求透传到你的 Node 进程 - 常见错误:直接访问
http://localhost:3000/ws返回 404,是因为没配置反向代理,upgrade 请求压根没到达 Nitro 的 WebSocket handler
典型 server/routes/ws.ts:
export default defineWebSocketHandler({
async upgrade(request) {
// 可校验 token、session、origin 等
if (!request.headers.get('sec-websocket-protocol')) {
throw createError({ statusCode: 400, statusMessage: 'Missing protocol' })
}
},
open(peer) {
console.log('peer connected:', peer.id)
},
message(peer, message) {
peer.send(`echo: ${message}`)
}
})
代理 WebSocket 到后端服务时,@nuxtjs/proxy 已过时
该模块自 Nuxt 3.7+ 起被官方弃用,且它根本**不支持 WebSocket 代理**(其底层是 http-proxy-middleware,对 Upgrade 请求支持极弱)。现在应改用:
-
Nginx 配置:在 location 块中显式设置
proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade" -
Caddy 2:天然支持 WebSocket 透传,只需
reverse_proxy+transport http即可 - Vercel / Netlify:不支持 WebSocket,必须换托管平台(如 Cloudflare Pages + Workers,或自建 Node 服务)
如果你坚持用本地开发代理,可用 nitro-dev-server 插件或直接写一个微型 Express 中间件挂载到 Nitro 的 server.handlers,但复杂度远高于直接起一个独立 ws 服务。
真正容易被忽略的一点:WebSocket 的鉴权不是靠 Cookie 自动携带的——new WebSocket(url) 不发送 Cookie,除非你显式配置 credentials: 'include'(但原生 API 不支持该选项),所以 token 必须放在 URL query 或首次 send() 的消息体里。Nitro 的 defineWebSocketHandler 中拿到的 request 是 upgrade request,能读 header 和 query,但无法像 HTTP route 那样用 event.context.auth——所有认证逻辑得自己手写解析。










