Workers SDK Issue Reports

← Back to Dashboard

#10390 Vite Plugin: Headers returned in Websocket handshake/upgrade are ignored

Recommendation:KEEP OPEN
Difficulty:medium
Reasoning:

Valid bug confirmed in code. Response headers from Worker WebSocket upgrade are ignored in websockets.ts. No fix in PRs/changelog. Reporter provided excellent reproduction.

Suggested Action:

Fix packages/vite-plugin-cloudflare/src/websockets.ts to forward response headers during WebSocket upgrade.

Analysis Report

Issue Review: cloudflare/workers-sdk#10390

Summary

WebSocket handshake/upgrade response headers from Workers are ignored by the Vite plugin - headers like Set-Cookie set in the 101 response are never sent to the browser.

Findings

  • Created: 2025-08-17
  • Updated: 2025-08-18
  • Version: @cloudflare/vite-plugin ^1.11.5 → 1.21.2 (current)
  • Component: vite-plugin-cloudflare (websockets.ts)
  • Labels: bug, vite-plugin
  • Comments: 0

Key Evidence

  • Code inspection confirms the bug still exists: In packages/vite-plugin-cloudflare/src/websockets.ts, the handleWebSocket function extracts response.webSocket but completely ignores response.headers
  • No PRs fix this issue: Searched for PRs mentioning #10390 and websocket-related keywords - no fixes found
  • No changelog entries: The vite-plugin-cloudflare CHANGELOG has no mentions of issue #10390 or websocket header fixes
  • Recent commits don't address this: 5 commits since the issue was created, none relate to websocket headers
  • Reporter provided excellent details: Reproduction repo, specific code reference, version info, and clear expected vs actual behavior

Root Cause Analysis

The bug is in packages/vite-plugin-cloudflare/src/websockets.ts at lines 37-51:

const response = await miniflare.dispatchFetch(url, {
  headers,
  method: request.method,
});
const workerWebSocket = response.webSocket;

if (!workerWebSocket) {
  socket.destroy();
  return;
}

nodeWebSocket.handleUpgrade(
  request,
  socket,
  head,
  async (clientWebSocket) => {
    void coupleWebSocket(clientWebSocket, workerWebSocket);
    nodeWebSocket.emit("connection", clientWebSocket, request);
  }
);

Problem: The response.headers are never used. When the worker returns a 101 response with headers like Set-Cookie or custom headers, those headers should be included in the WebSocket upgrade response sent to the client. However, nodeWebSocket.handleUpgrade() doesn't automatically forward these headers.

Proposed Solution

The ws library's handleUpgrade doesn't directly support custom headers, but you can write headers to the socket before completing the upgrade. Here's a suggested fix:

nodeWebSocket.handleUpgrade(
  request,
  socket,
  head,
  async (clientWebSocket) => {
    // Forward headers from the worker response to the client
    // Note: Some headers should be excluded as they're handled by the upgrade
    const excludedHeaders = new Set([
      'connection',
      'upgrade', 
      'sec-websocket-accept',
      'sec-websocket-extensions',
      'sec-websocket-protocol',
    ]);
    
    // Write additional headers to the socket
    // The ws library has already written the base 101 response headers
    // We need to inject headers before the final \r\n
    const extraHeaders: string[] = [];
    for (const [key, value] of response.headers.entries()) {
      if (!excludedHeaders.has(key.toLowerCase())) {
        extraHeaders.push(`${key}: ${value}`);
      }
    }
    
    if (extraHeaders.length > 0) {
      // This approach won't work with handleUpgrade as-is
      // Alternative: use completeUpgrade or manual upgrade handling
    }

    void coupleWebSocket(clientWebSocket, workerWebSocket);
    nodeWebSocket.emit("connection", clientWebSocket, request);
  }
);

Better approach: Use the ws library's lower-level API or implement custom upgrade handling similar to how Wrangler does it. Looking at how wrangler dev handles this would provide the correct pattern.

The fix likely requires:

  1. Not using handleUpgrade and instead doing a manual WebSocket upgrade
  2. Writing the HTTP 101 response with all headers from the Worker response
  3. Then establishing the WebSocket connection

Recommendation

Status: KEEP OPEN

Reasoning: This is a valid, reproducible bug with clear code evidence. The issue prevents developers from testing WebSocket authentication flows (Set-Cookie headers) in local development with the Vite plugin. The reporter provided a minimal reproduction and pinpointed the exact file/line causing the issue.

Action: This bug needs to be fixed in packages/vite-plugin-cloudflare/src/websockets.ts. Compare with Wrangler's WebSocket upgrade handling to ensure parity.

Implementation Details

Difficulty: Medium

  • The fix requires understanding both the ws library's API and the HTTP WebSocket upgrade protocol
  • Need to maintain compatibility with existing behavior while adding header forwarding
  • Testing WebSocket upgrades with headers requires some setup

Files to Modify:

  • packages/vite-plugin-cloudflare/src/websockets.ts - main fix location

Testing Recommendations:

  1. Use the reporter's reproduction repo: https://github.com/cloudkite/websocket-handshake
  2. Add test cases for:
    • Set-Cookie headers in WebSocket upgrade response
    • Custom headers (e.g., X-Custom-Header)
    • Verify excluded headers (Connection, Upgrade, etc.) are not duplicated
  3. Compare behavior with wrangler dev to ensure parity

Notes & Feedback (0)

No notes yet.

Add Note