What does a server send when a WebSocket upgrade request names a Sec-WebSocket-Version it does not support?
answer
- settled once, before any switch
- the refusal is an ordinary response
- the client asserts, the server counter-offers
- the offered list makes it actionable
- no socket, so no close code
basics
~20 sIt aborts the handshake and returns an ordinary HTTP error response - the specification names 426 Upgrade Required - carrying a Sec-WebSocket-Version field that lists every version it is willing to speak. The client may retry with one of them.
solid answer
~40 sThe client states one version on its request, `Sec-WebSocket-Version: 13`, which is the version RFC 6455 defines. If the server does not understand that version it must abort the handshake and send an appropriate HTTP error status — the one the specification names is `426 Upgrade Required` — together with a `Sec-WebSocket-Version` field listing every version it *is* willing to use. That refusal is an ordinary HTTP response: it has a status line, it may have a body, no protocol switch happened, and there is no socket and therefore no close frame. A client that reads the offered versions may retry the handshake naming one of them; otherwise it gives up. Note the direction: the client names a version first, and the server offers alternatives only when refusing.
code
http · 3 linesHTTP/1.1 426 Upgrade Required
Sec-WebSocket-Version: 13
Content-Length: 0go deeper
Recall that the client states Sec-WebSocket-Version: 13 on the upgrade request, and that a server unable to speak it answers with an HTTP error rather than opening a socket.
Explain the exchange: the client asserts one version, the refusal carries a Sec-WebSocket-Version field listing what the server accepts, and the client may retry naming one of those.
Show that you read a 426 as a precise finding - the request reached something that recognised the handshake and declined the version - rather than as a generic connection failure.
The angle is that the whole failure path stays ordinary HTTP, which is what lets version disagreements be observed and resolved with the same tooling as any other request.
## One number, sent once Every WebSocket opening handshake carries `Sec-WebSocket-Version` on the request, and in practice its value is always `13` — the version RFC 6455 defines. The field exists because the protocol went through several pre-standard revisions before it was published, and endpoints written against those revisions had to be distinguishable from endpoints written against the finished specification. The important structural point is **when** this is settled: the version is stated on the request and resolved before any protocol switch occurs. There is no in-band renegotiation afterwards, because after a `101` the connection is no longer speaking HTTP and has no mechanism for one. ## The refusal When the server does not understand the version it was given, it aborts the handshake and answers with an appropriate HTTP error status — the specification names **`426 Upgrade Required`** — and includes a `Sec-WebSocket-Version` field enumerating every version it is willing to use. That field is the whole point of the refusal: without it the client learns only that it failed, not what would succeed. Two details are easy to state backwards: - **The client names one version; the server offers a list.** It is not a menu the server publishes first and the client chooses from. The client asserts, the server either proceeds or counter-offers. - **The refusal carries no accept digest.** `Sec-WebSocket-Accept` appears only on a `101`. A 426 that somehow carried one would be meaningless, because nothing was accepted. ## The refusal is an ordinary HTTP response This is where the surrounding ambiguity bites, because three unrelated things in this area are called "close" or "status": | What arrived | Layer | What it is | |---|---|---| | `426 Upgrade Required` with a version list | HTTP response | A refused handshake; no socket ever existed | | `101 Switching Protocols` | HTTP response | The accepted handshake; the last HTTP bytes on the connection | | A close status code in the 1000-range | WebSocket control frame | The end of a socket that *did* exist - a different namespace entirely | A refused upgrade produces the first row and never the third. Expecting a WebSocket close code on a handshake that was rejected is a sign the candidate has not separated the two layers. The same applies to the word *upgrade* in the status name: `426 Upgrade Required` here is about the **WebSocket protocol version**, not about upgrading TLS, not about the HTTP version, and not about a client fleet moving to a newer release. ## What the client does next 1. Read the `Sec-WebSocket-Version` field on the refusal to see what the server will accept. 2. If it can speak one of the offered versions, build a fresh handshake request naming that version — including a **new** random `Sec-WebSocket-Key`, since the nonce is per connection attempt. 3. If it cannot, report the failure. Retrying the identical request is pointless: the refusal is deterministic and nothing about the second attempt will differ. ## Why this is rarely seen, and where it still shows up Because the published version has been stable for a long time and every conforming client sends `13`, most engineers never see a version refusal in production. It stays worth knowing for two reasons. - **It is the model for how this handshake refuses anything.** The refusal is an ordinary HTTP response with a status and an explanatory field, delivered before any switch. Nothing exotic happens on the failure path, which is exactly what makes the failure path easy to observe with ordinary request tooling. - **A 426 in a log is a precise finding.** It means the request reached something that understood it was a WebSocket handshake and declined the version — a far narrower diagnosis than a generic connection failure, and one that immediately names the field to compare on both sides. The interview-grade version of the answer is short: the client asserts `13`, an unsupporting server returns an HTTP error status — `426 Upgrade Required` is the one the specification names — carrying the versions it accepts, and the client may retry with one of those. No socket existed at any point, no bytes were ever exchanged outside HTTP, and the whole disagreement is visible in one request and one response.
- Can the refusal offer more than one version?Yes. The server lists every version it is willing to use, and a client that speaks one of them may retry the handshake naming that version. The list is what makes the refusal actionable rather than merely final.
- Is a refused upgrade reported with a WebSocket close status code?No. Close status codes belong to a socket that exists, and a refused handshake never produced one. The refusal is an ordinary HTTP response with an HTTP status line, which is why ordinary request tooling can see it.
- Should the retry reuse the original Sec-WebSocket-Key?No. The key is a fresh random nonce per connection attempt, so the retry generates a new one and expects the digest to be computed from that. Reusing the old key weakens the one property the digest actually binds: freshness.
saying these in an interview costs you the question
- Thinks 426 Upgrade Required refers to upgrading TLS or the HTTP version
- Says the protocol version is negotiated after the socket opens
- Expects a WebSocket close status code on a refused upgrade
- Retries the identical request unchanged after a version refusal
- Believes the server publishes a version list the client picks from first