eclipse-vertx/vert.x · error · WebSocketHandshakeException

Invalid connection header

Error message

Invalid connection header

What it means

This WebSocketHandshakeException is thrown in createHandshaker when a WebSocket upgrade request lacks a "Connection" header containing "Upgrade". The HTTP/1.1 upgrade handshake requires both "Connection: Upgrade" and "Upgrade: websocket" headers. Vert.x rejects the request with a 400 response before the handshaker is created.

Source

Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/http1/Http1ServerConnection.java:375

        } catch (WebSocketHandshakeException e) {
          promise.fail(e);
          return;
        }
        promise.complete(new ServerWebSocketHandshaker(request, handshaker, webSocketConfig, registerWebSocketWriteHandlers));
      }
    });
  }

  public WebSocketServerHandshaker createHandshaker(Http1ServerRequest request) throws WebSocketHandshakeException {
    // As a fun part, Firefox 6.0.2 supports Websockets protocol '7'. But,
    // it doesn't send a normal 'Connection: Upgrade' header. Instead it
    // sends: 'Connection: keep-alive, Upgrade'. Brilliant.
    String connectionHeader = request.getHeader(io.vertx.core.http.HttpHeaders.CONNECTION);
    if (connectionHeader == null || !connectionHeader.toLowerCase().contains("upgrade")) {
      request.response()
        .setStatusCode(BAD_REQUEST.code())
        .end("\"Connection\" header must be \"Upgrade\".");
      throw new WebSocketHandshakeException("Invalid connection header");
    }
    if (request.method() != io.vertx.core.http.HttpMethod.GET) {
      request.response()
        .setStatusCode(METHOD_NOT_ALLOWED.code())
        .end();
      throw new WebSocketHandshakeException("Invalid HTTP method");
    }
    String wsURL;
    try {
      wsURL = HttpUtils.getWebSocketLocation(request, isSsl());
    } catch (Exception e) {
      request.response()
        .setStatusCode(BAD_REQUEST.code())
        .end("Invalid request URI");
      throw new WebSocketHandshakeException("Invalid WebSocket location", e);
    }
    String subProtocols = null;
    if (webSocketConfig.getSubProtocols() != null) {

View on GitHub (pinned to fb308bd8c3)

Solutions

  1. Ensure the client sends "Connection: Upgrade" and "Upgrade: websocket" headers with the GET request.
  2. If a proxy sits in front, configure it to forward the Connection and Upgrade headers (e.g. nginx proxy_set_header Connection $connection_upgrade).
  3. Inspect the actual incoming headers (request.headers()) to confirm what the client sent before upgrading.
  4. Return a clear 400 to the client when the handshake fails so the client-side code can surface the misconfiguration.

Example fix

// before (client-side)
request.putHeader("Upgrade", "websocket");

// after
request.putHeader("Upgrade", "websocket");
request.putHeader("Connection", "Upgrade");
Defensive patterns

Strategy: validation

Validate before calling

String conn = request.getHeader(HttpHeaders.CONNECTION);
if (conn == null || !conn.toLowerCase().contains("upgrade")) {
  // reject before upgrade
}

Try / catch

try {
  serverRequest.toWebSocket();
} catch (WebSocketHandshakeException e) {
  log.warn("Bad upgrade request: " + e.getMessage());
}

Prevention

When it happens

Trigger: Calling server upgrade methods (upgrade/createWebSocket path) with an incoming request whose Connection header is missing or does not contain "upgrade" (case-insensitive check), e.g. "Connection: keep-alive" only.

Common situations: Hand-rolled WebSocket clients forgetting the Connection header; proxies or intermediaries stripping or rewriting the Connection header; testing the upgrade endpoint with curl or a plain GET that lacks proper upgrade headers.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06). Data as JSON: /api/errors/db5b85fe5b9b95dd. Report an issue: GitHub.