{"record":{"id":"f0897b0aeef6606e","repo":"denoland/deno","slug":"invalid-header-connection-header-must-contain","errorCode":null,"errorMessage":"Invalid Header: 'connection' header must contain 'Upgrade'","messagePattern":"Invalid Header: 'connection' header must contain 'Upgrade'","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/http/02_websocket.ts","lineNumber":56,"sourceCode":"function upgradeWebSocket(request, options = { __proto__: null }) {\n  const inner = toInnerRequest(request);\n  if (inner._wantsUpgrade) {\n    inner._throwIfUpgraded();\n  }\n  const upgrade = request.headers.get(\"upgrade\");\n  const upgradeHasWebSocketOption = upgrade !== null &&\n    websocketCvf(upgrade);\n  if (!upgradeHasWebSocketOption) {\n    throw new TypeError(\n      \"Invalid Header: 'upgrade' header must contain 'websocket'\",\n    );\n  }\n\n  const connection = request.headers.get(\"connection\");\n  const connectionHasUpgradeOption = connection !== null &&\n    upgradeCvf(connection);\n  if (!connectionHasUpgradeOption) {\n    throw new TypeError(\n      \"Invalid Header: 'connection' header must contain 'Upgrade'\",\n    );\n  }\n\n  const websocketKey = request.headers.get(\"sec-websocket-key\");\n  if (websocketKey === null) {\n    throw new TypeError(\n      \"Invalid Header: 'sec-websocket-key' header must be set\",\n    );\n  }\n\n  const accept = op_http_websocket_accept_header(websocketKey);\n\n  const r = newInnerResponse(101);\n  r.headerList = [\n    [\"upgrade\", \"websocket\"],\n    [\"connection\", \"Upgrade\"],\n    [\"sec-websocket-accept\", accept],","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/http/02_websocket.ts#L38-L74","documentation":"Second handshake check in `Deno.upgradeWebSocket`: the `Connection` header's comma-separated values must include the token `Upgrade` (case-insensitive), because RFC 6455 requires the client to opt in with `Connection: Upgrade`. A missing or rewritten header throws a TypeError before the socket is created.","triggerScenarios":"Intermediaries that rewrite `Connection: keep-alive, Upgrade` to plain `keep-alive`; HTTP/2 requests (which forbid the Connection header entirely); hand-rolled clients sending only `Upgrade: websocket` without the Connection token.","commonSituations":"Reverse proxies and WAFs normalizing hop-by-hop headers; API gateways terminating WebSocket-incompatible frontends; testing with HTTP/2-enabled tooling.","solutions":["Configure the proxy to pass the token: `proxy_set_header Connection \"upgrade\"`","Ensure clients use a real WebSocket implementation (browser WebSocket API or a ws library)","Check both `upgrade` and `connection` headers before calling `upgradeWebSocket`, and answer 400 otherwise"],"exampleFix":"// before\nconst ws = Deno.upgradeWebSocket(request);\n\n// after\nconst conn = request.headers.get(\"connection\") ?? \"\";\nif (!/upgrade/i.test(conn)) {\n  return new Response(\"Connection: Upgrade required\", { status: 400 });\n}\nconst ws = Deno.upgradeWebSocket(request);","handlingStrategy":"validation","validationCode":"const conn = request.headers.get(\"connection\") ?? \"\";\nconst hasUpgradeToken = conn.split(\",\").some((v) => v.trim().toLowerCase() === \"upgrade\");\nif (!hasUpgradeToken) return new Response(\"Connection: Upgrade required\", { status: 400 });\nconst ws = Deno.upgradeWebSocket(request);","typeGuard":"const connectionHasUpgrade = (req: Request): boolean =>\n  (req.headers.get(\"connection\") ?? \"\")\n    .split(\",\")\n    .some((v) => v.trim().toLowerCase() === \"upgrade\");","tryCatchPattern":"try {\n  return Deno.upgradeWebSocket(request);\n} catch (err) {\n  if (err instanceof TypeError && err.message.includes(\"connection\")) {\n    return new Response(\"Bad handshake\", { status: 400 });\n  }\n  throw err;\n}","preventionTips":["Do not terminate WebSocket traffic behind HTTP/2-only frontends (Connection header is illegal in h2)","Verify proxy config passes the Connection header token through","Test the full handshake with a real WebSocket client, not plain HTTP tools"],"tags":["websocket","http","headers","proxy"],"backgroundTag":"websocket-upgrade-failed","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}