Deno `upgradeWebSocket` missing `Sec-WebSocket-Protocol` header in response
What version of Hono are you using?
4.9.8
What runtime/platform is your app running on? (with version if possible)
Deno 2.5.2
What steps can reproduce the bug?
- Create a WebSocket server using Deno's
upgradeWebSocket - Open a WebSocket with a protocol (e.g.
new WebSocket("...", ["foobar"])) - Attempt to connect to the WebSocket from Chrome
What is the expected behavior?
The WebSocket should work
What do you see instead?
Chrome gives a nondescript WebSocket error and disconnects immediately
Additional information
This seems to be specific to Deno. The root issue is that Sec-WebSocket-Protocol is not being set in the response.
Firefox does not return an error, this issue is specific to Chrome.
Here is the output comparing the Node upgradeWebSocket and the Deno upgradeWebSocket:
=== NODE===
> GET /connect/websocket HTTP/1.1
> Host: localhost:6420
> Accept: */*
> Upgrade: websocket
> Origin: http://localhost:5173
> Cache-Control: no-cache
> Accept-Language: en-US,en;q=0.9
> Pragma: no-cache
> Connection: Upgrade
> Sec-WebSocket-Key: sixpk706F01Oc4x9UXezjA==
> User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
> Sec-WebSocket-Version: 13
> Sec-WebSocket-Protocol: rivet, rivet_target.actor, rivet_actor.19c4f9038947cdcb, rivet_encoding.bare
> Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
>
* Request completely sent off
< HTTP/1.1 101 Switching Protocols
< Upgrade: websocket
< Connection: Upgrade
< Sec-WebSocket-Accept: OHBDEZ0/KVHZeTpQcokFETWongc=
< Sec-WebSocket-Protocol: rivet
<
=== DENO ===
> GET /connect/websocket HTTP/1.1
> Host: localhost:8080
> Accept: */*
> Upgrade: websocket
> Origin: http://localhost:5173
> Cache-Control: no-cache
> Accept-Language: en-US,en;q=0.9
> Pragma: no-cache
> Connection: Upgrade
> Sec-WebSocket-Key: sixpk706F01Oc4x9UXezjA==
> User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
> Sec-WebSocket-Version: 13
> Sec-WebSocket-Protocol: rivet, rivet_target.actor, rivet_actor.19c4f9038947cdcb, rivet_encoding.bare
> Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
>
* Request completely sent off
< HTTP/1.1 101 Switching Protocols
< connection: Upgrade
< sec-websocket-accept: OHBDEZ0/KVHZeTpQcokFETWongc=
< upgrade: websocket
< access-control-allow-credentials: true
< access-control-allow-origin: http://localhost:5173
< vary: Origin
< date: Fri, 03 Oct 2025 18:55:06 GMT
<
We're using this middleware to workaround this issue for now:
router.use(
"*",
createMiddleware(async (c, next) => {
const upgrade = c.req.header("upgrade");
const isWebSocket = upgrade?.toLowerCase() === "websocket";
const isGet = c.req.method === "GET";
if (isGet && isWebSocket) {
c.header("Sec-WebSocket-Protocol", "YOUR PROTOCOL");
}
await next();
}),
);
I have not investigated other WebSocket drivers. We implement a similar fix to above in Cloudflare Workers, so I suspect Deno is not the only driver affected.
Hi @NathanFlurry
Is this Hono-specific problem? Can you try it without Hono by referring to the link?
https://docs.deno.com/examples/http_server_websocket/
I tried it quickly, but the behavior is the same for both with Hono and without Hono.