Skip to content

[miniflare] fix: preserve multiple Set-Cookie headers on WebSocket 101 upgrade responses#14330

Open
matingathani wants to merge 7 commits into
cloudflare:mainfrom
matingathani:fix/miniflare-websocket-set-cookie
Open

[miniflare] fix: preserve multiple Set-Cookie headers on WebSocket 101 upgrade responses#14330
matingathani wants to merge 7 commits into
cloudflare:mainfrom
matingathani:fix/miniflare-websocket-set-cookie

Conversation

@matingathani

@matingathani matingathani commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Fixes #14145.

Iterating a Headers object with for…of collapses all Set-Cookie values into a single comma-joined string — a known web-platform limitation. When a Worker (or custom service binding) returned multiple Set-Cookie headers on a 101 upgrade response, the cookies were emitted as a single mangled line instead of separate Set-Cookie lines. This corrupts cookies whose attributes contain commas (e.g. Expires=Wed, 09 Jun 2026 10:18:14 GMT), causing clients to receive an unparseable cookie.

Fix: use extra.getSetCookie() to emit each cookie as its own header line, then iterate the remaining headers with set-cookie skipped. This mirrors the approach already in packages/vite-plugin-cloudflare/src/websockets.ts (landed in #14117).


  • Tests
    • Tests included/updated
  • Public documentation
    • Documentation not necessary because: bug fix for internal WebSocket header forwarding, no user-facing API change

Open in Devin Review

…1 upgrade responses

Iterating a `Headers` object with `for…of` collapses all `Set-Cookie` values
into a single comma-joined string. This corrupted cookies whose attributes
contain commas (e.g. `Expires=Wed, 09 Jun 2026 …`), meaning clients received
a single mangled `Set-Cookie` line instead of two separate cookies.

Fix: use `extra.getSetCookie()` first to emit each cookie as its own header
line, then iterate the rest of the headers with `set-cookie` skipped.

Mirrors the approach already in packages/vite-plugin-cloudflare/src/websockets.ts.

Fixes cloudflare#14145
Copilot AI review requested due to automatic review settings June 17, 2026 05:28
@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 146471d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
miniflare Patch
@cloudflare/deploy-helpers Patch
@cloudflare/pages-shared Patch
@cloudflare/vite-plugin Patch
@cloudflare/vitest-pool-workers Patch
wrangler Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk Jun 17, 2026
@workers-devprod workers-devprod requested review from a team and penalosa and removed request for a team June 17, 2026 05:29
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/fix-miniflare-websocket-set-cookie.md: [@cloudflare/wrangler]
  • packages/miniflare/src/index.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/index.spec.ts: [@cloudflare/wrangler]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

- Fix TS2322: use block body in onTestFinished callback so server.close()
  return value is not implicitly returned as Awaitable<void>
- Also close WebSocketServer in cleanup to release all handles
- Fix comment: first cookie (not second) has the Expires comma
- Drop `fix:` prefix from changeset title per AGENTS.md convention
@pkg-pr-new

pkg-pr-new Bot commented Jun 17, 2026

Copy link
Copy Markdown
create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@14330

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@14330

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@14330

miniflare

npm i https://pkg.pr.new/miniflare@14330

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@14330

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@14330

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@14330

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@14330

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@14330

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@14330

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@14330

wrangler

npm i https://pkg.pr.new/wrangler@14330

commit: 146471d

`getSetCookie()` is not part of the standard Fetch API. Guard with
`typeof extra.getSetCookie === "function"` before calling it, matching
the same defensive check in packages/vite-plugin-cloudflare/src/websockets.ts.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

Open in Devin Review

Comment on lines +1102 to +1106
// Preserve multiple Set-Cookie values verbatim — iterating `Headers`
// with `for…of` collapses them into a single comma-joined string
// (web-platform limitation), which corrupts cookies containing commas
// (e.g. `Expires=Wed, 09 Jun 2026 …`). `getSetCookie` is not part of
// the standard Fetch API, so guard before calling it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚩 Undici 7.x may already handle Set-Cookie separately in iteration

The changeset and code comment state that iterating a Headers object with for...of collapses Set-Cookie values into a single comma-joined string. This was true for older implementations, but undici 7.x follows the updated Fetch spec (since March 2023) where Headers[Symbol.iterator]() yields each Set-Cookie value as a separate entry. This means the bug being fixed might only have manifested with older undici versions or specific workerd Headers implementations. The fix using getSetCookie() is still the most robust approach and explicitly correct regardless of iteration behavior, so this doesn't affect correctness — but the comment's characterization of the problem may not accurately describe the current runtime behavior.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

devin-ai-integration[bot]

This comment was marked as resolved.

matingathani and others added 3 commits June 17, 2026 13:21
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

miniflare: WebSocket 101 upgrade response collapses multiple Set-Cookie headers

3 participants