Skip to content

Lowercase "upgrade" value in Connection header may prevent connection upgrade #42

Description

@pzingg
  1. Description of issue

I wasted several hours today trying to figure out why MintWebSocket.upgrade/5 was failing when connecting to a websocket at wss://demo.rss.chat/

I noted that other websocket clients were succeeding and went to compare the headers that were sent at the upgrade request.

This particular server rejects the standard Mint.WebSocket.upgrade/5 call with a status 400 and a somewhat misleading message:

HTTP/1.1 400 Bad Request
Alt-Svc: h3=":443"; ma=2592000
Content-Type: text/html
Date: Mon, 17 Aug 2026 00:13:04 GMT
Server: Caddy
Transfer-Encoding: chunked

34
This HTTP server can't handle WebSocket connections.

I finally tracked down the issue to the fact that MintWebSocket.upgrade/5 sends a Connection header with the value "upgrade" in lowercase. When that is changed to "Upgrade" (capitalized), the socket is upgraded successfully.

  1. Possible fix

Mint.WebSocket.Utils.headers/2 shows the following:

  # header names are written in their conventional casing: Mint lowercases them
  # unless the connection was opened with `case_sensitive_headers: true`
  def headers({:http1, nonce}, extensions) when is_binary(nonce) do
    [
      {"Upgrade", "websocket"},
      {"Connection", "upgrade"},
      {"Sec-WebSocket-Version", "13"},
      {"Sec-WebSocket-Key", nonce},
      {"Sec-WebSocket-Extensions", extension_string(extensions)}
    ]
    |> Enum.reject(fn {_k, v} -> v == "" end)
  end

When I changed {"Connection", "upgrade"} to {"Connection", "Upgrade"} (capitalized), the connection succeeded. All of the non-normative examples in RFC6445 section 1.1 and 1.2 show the handshake headers using the capitalized "Upgrade" and the lowercase "websocket".

I believe that Mint.WebSocket.Utils.headers/2 should return the capitalized version.

For this particular server I was able to connect by supplying my own {"Connection", "Upgrade"} header (so that both values, "upgrade" and "Upgrade", are sent to the server in that order), but I don't know if that will work generally or not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions