denoland/deno · error · TypeError
Invalid Header: 'connection' header must contain 'Upgrade'
Error message
Invalid Header: 'connection' header must contain 'Upgrade'
What it means
Second handshake check in Deno.upgradeWebSocket: the 'connection' header must contain the token 'Upgrade' (case-insensitive comma list, via upgradeCvf). This is part of RFC 6455's handshake (Connection: Upgrade); its absence means the request is not a protocol upgrade request.
Source
Thrown at ext/http/02_websocket.ts:56
function upgradeWebSocket(request, options = { __proto__: null }) {
const inner = toInnerRequest(request);
if (inner._wantsUpgrade) {
inner._throwIfUpgraded();
}
const upgrade = request.headers.get("upgrade");
const upgradeHasWebSocketOption = upgrade !== null &&
websocketCvf(upgrade);
if (!upgradeHasWebSocketOption) {
throw new TypeError(
"Invalid Header: 'upgrade' header must contain 'websocket'",
);
}
const connection = request.headers.get("connection");
const connectionHasUpgradeOption = connection !== null &&
upgradeCvf(connection);
if (!connectionHasUpgradeOption) {
throw new TypeError(
"Invalid Header: 'connection' header must contain 'Upgrade'",
);
}
const websocketKey = request.headers.get("sec-websocket-key");
if (websocketKey === null) {
throw new TypeError(
"Invalid Header: 'sec-websocket-key' header must be set",
);
}
const accept = op_http_websocket_accept_header(websocketKey);
const r = newInnerResponse(101);
r.headerList = [
["upgrade", "websocket"],
["connection", "Upgrade"],
["sec-websocket-accept", accept],View on GitHub (pinned to 89f33cbef2)
Solutions
- Guard before upgrading: parse req.headers.get('connection') and require the 'upgrade' token (case-insensitive) before calling Deno.upgradeWebSocket.
- Configure the proxy to set Connection: upgrade when forwarding (nginx: proxy_set_header Connection "upgrade").
- Use a spec-compliant WebSocket client library rather than hand-written handshake code.
Example fix
// before
const { response } = Deno.upgradeWebSocket(req); // Connection: keep-alive -> throws
// after
const conn = req.headers.get("connection") ?? "";
if (!conn.toLowerCase().split(',').map((s) => s.trim()).includes("upgrade")) {
return new Response("not a websocket upgrade", { status: 400 });
}
const { socket, response } = Deno.upgradeWebSocket(req); Defensive patterns
Strategy: validation
Validate before calling
const connectionTokens = (req.headers.get("connection") ?? "")
.toLowerCase().split(",").map((s) => s.trim());
if (!connectionTokens.includes("upgrade")) {
return new Response("Connection: Upgrade required", { status: 400 });
} Type guard
function hasConnectionUpgrade(req: Request): boolean { return (req.headers.get("connection") ?? "").toLowerCase().split(",").map((s) => s.trim()).includes("upgrade"); } Try / catch
try { return Deno.upgradeWebSocket(req).response; } catch (e) { if (e instanceof TypeError && e.message.includes("'connection' header")) { return new Response("expected Connection: Upgrade", { status: 400 }); } throw e; } Prevention
- Configure proxies to re-set hop-by-hop headers (proxy_set_header Connection "upgrade").
- Validate the full handshake triple (upgrade/connection/key) in one helper before upgrading.
When it happens
Trigger: Handshake requests where a client or intermediary sent Upgrade: websocket but Connection: keep-alive/close; proxies that forward one hop header but rewrite Connection; hand-crafted HTTP clients that set only the upgrade header; HTTP/2 pseudo-upgrade attempts mapped onto HTTP/1 semantics without the header.
Common situations: Misconfigured reverse proxies or CDNs that drop the Connection header (hop-by-hop header, must be explicitly re-set); custom embedded HTTP clients; API gateways terminating WS.
Related errors
- Invalid Header: 'upgrade' header must contain 'websocket'
- Invalid Header: 'sec-websocket-key' header must be set
- Protocol '${options.protocol}' not in the request's protocol
- BenchContext::start() has already been invoked
- Vary header must not contain '*'
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/f0897b0aeef6606e.
Report an issue: GitHub.