Skip to content

fix: forward Hono response headers during WebSocket upgrade#346

Merged
yusukebe merged 1 commit intohonojs:mainfrom
gentamura:fix/websocket-response-headers
Apr 30, 2026
Merged

fix: forward Hono response headers during WebSocket upgrade#346
yusukebe merged 1 commit intohonojs:mainfrom
gentamura:fix/websocket-response-headers

Conversation

@gentamura
Copy link
Copy Markdown
Contributor

Summary

Forward headers attached to the Response returned by the Hono app to the WebSocket upgrade response, so headers set by middleware (e.g. Set-Cookie, custom auth headers, WWW-Authenticate on reject) are no longer dropped during the handshake.

Previously, in src/websocket.ts the Response returned from fetchCallback(...) was inspected only for statusresponse.headers was discarded on both the successful and rejected paths.

Changes

  • On a successful upgrade, response headers are appended via wss.on('headers', ...) (the official ws API for injecting handshake headers). The listener is removed in finally; headers is emitted synchronously inside handleUpgrade, so it cannot leak across concurrent upgrades on the shared wss.
  • On a rejected upgrade, response headers are written into the manual socket.end(...) HTTP response produced by rejectUpgradeRequest.
  • Skip hop-by-hop headers per RFC 9110 Section 7.6.1 (connection, keep-alive, proxy-authenticate, proxy-authorization, te, trailer, transfer-encoding, upgrade), framing (content-length), and WebSocket handshake headers managed by ws (sec-websocket-accept, sec-websocket-extensions, sec-websocket-protocol) to avoid corrupting the handshake.

Tests

Two new tests in test/websocket.test.ts:

  • A custom response header set by middleware reaches the client on a successful upgrade.
  • A custom response header is preserved on a rejected (401) upgrade response.

Verification

  • bun run test --run test/websocket.test.ts (5/5 passing)
  • bun run format
  • bun run lint (pre-existing warning in listener.ts only, unrelated to this change)
  • bun run build

Context

A companion PR is open against honojs/middleware for the same bug in @hono/node-ws: honojs/middleware#1873. Since @hono/node-ws is being deprecated in favor of @hono/node-server's built-in upgradeWebSocket (per honojs/middleware#1862), porting the fix here ensures the bug does not survive the migration.

Headers attached to the Response returned by the Hono app
(e.g. `Set-Cookie`, custom auth headers, `WWW-Authenticate` on reject)
were dropped during the WebSocket handshake on both successful and
rejected upgrades.

- Successful upgrade: append response headers via `wss.on('headers', ...)`,
  the official `ws` API for injecting handshake headers. The listener is
  removed in `finally`; `headers` is emitted synchronously inside
  `handleUpgrade`, so it cannot leak across concurrent upgrades on the
  shared `wss`.
- Rejected upgrade: include response headers in the manual
  `socket.end(...)` HTTP response written by `rejectUpgradeRequest`.
- Skip hop-by-hop headers per RFC 9110 Section 7.6.1, framing
  (`content-length`), and WebSocket handshake headers managed by `ws`
  (`sec-websocket-accept`, `sec-websocket-extensions`, `sec-websocket-protocol`)
  to avoid corrupting the handshake.
Copy link
Copy Markdown
Member

@yusukebe yusukebe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@yusukebe
Copy link
Copy Markdown
Member

@gentamura

Thanks! Good PR. I'll merge and release a new version now.

Regarding honojs/middleware#1873, I think we can close it as the @hono/node-ws is already deprecated.

@yusukebe yusukebe merged commit 0ed7656 into honojs:main Apr 30, 2026
5 checks passed
@gentamura gentamura deleted the fix/websocket-response-headers branch May 1, 2026 05:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants