WebSocket Upgrade Failure PCAP Analysis

How to troubleshoot WebSocket upgrade failures with packet captures, including HTTP 101, Upgrade headers, Connection headers, proxy stripping, TLS, resets, and idle timeouts.

websocket upgrade failed, 101 switching protocols, proxy websocket, connection upgrade, pcap analysis, http troubleshooting

<!-- multilingual-opening-answer -->

For “WebSocket Upgrade Failure PCAP Analysis”, the short answer is: How to troubleshoot WebSocket upgrade failures with packet captures, including HTTP 101, Upgrade headers, Connection headers, proxy stripping, TLS, resets, and idle timeouts. Treat that statement as a result to verify, not as a promise that every input, device, project, or environment behaves identically. A complete result records the starting state, the exact action, the visible output, and the condition that proves the task is finished in PCAP Surgery.

A site can serve ordinary HTTP normally while its live channel reports “WebSocket connection failed,” an unexpected response code, or a missing 101 Switching Protocols. In the capture, verify the Upgrade and Connection request headers, the proxy's response, TLS completion, and whether a reset occurs before or after the protocol switch; that separates an nginx WebSocket proxy-header problem from an application that closes an established socket. Encrypted payloads may hide header details without keys, but connection timing and TLS records still bound where the failure occurred. PCAP Surgery is useful because WebSocket traces often need both the HTTP handshake and the post-upgrade TCP timeline preserved.

What a healthy WebSocket upgrade looks like

A client sends an HTTP request with upgrade headers:

GET /socket HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ...
Sec-WebSocket-Version: 13

The server replies:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: ...

After that, the connection is no longer ordinary HTTP request/response traffic. It carries WebSocket frames.

Common upgrade failures

Common causes include:

  • Proxy strips Upgrade header.
  • Proxy strips or rewrites Connection: Upgrade.
  • Backend route does not support WebSocket.
  • TLS termination sends request to wrong upstream.
  • HTTP/2 to HTTP/1.1 upgrade behavior is misconfigured.
  • Authentication redirect happens instead of 101.
  • Backend returns 400, 403, 404, 426, 502, or 504.
  • Connection resets after upgrade.
  • Idle timeout closes quiet WebSocket.

The status code and headers matter.

Proxy header problems

Reverse proxies must forward WebSocket upgrade headers correctly. If the backend never sees Upgrade: websocket, it may treat the request as ordinary HTTP.

Packet evidence:

  • Client-to-proxy request includes upgrade headers.
  • Proxy-to-backend request lacks them.
  • Backend returns normal HTTP response instead of 101.

That is a proxy configuration problem, not a WebSocket client bug.

TLS and SNI

For secure WebSocket (wss://), TLS happens before HTTP upgrade. If TLS fails, the WebSocket handshake never begins. Preserve DNS, TCP, TLS ClientHello, SNI, and any TLS alert or reset.

Do not diagnose upgrade headers until the TLS path is proven.

Connection drops after 101

Sometimes upgrade succeeds, then the connection closes. That is a different failure.

Look for:

  • FIN or RST sender.
  • Idle timeout duration.
  • WebSocket ping/pong activity.
  • Proxy read timeout.
  • TCP retransmissions.
  • Zero window.
  • Backend process restart.

If the drop happens at a fixed interval, timeout policy is likely.

Checklist

Use this workflow:

  1. Preserve DNS and TCP connect.
  2. Verify TLS handshake for wss://.
  3. Inspect client upgrade request headers.
  4. Inspect server response status.
  5. Confirm 101 Switching Protocols if expected.
  6. Compare client-to-proxy and proxy-to-backend requests.
  7. Look for redirects or auth responses.
  8. If upgrade succeeds, inspect post-upgrade FIN/RST/timeout.
  9. Preserve WebSocket ping/pong timing if visible.
  10. Trim only after keeping the full handshake.

Final diagnosis

WebSocket upgrade failures are usually HTTP handshake or proxy-forwarding problems until 101 Switching Protocols is proven. After upgrade, failures become long-lived TCP timeout, reset, or application protocol problems.

PCAP Surgery helps preserve both phases so a vague WebSocket error can be traced to headers, proxy behavior, TLS, backend response, or connection lifetime.

Upgrade response decision table

The HTTP status is a routing clue, not the complete diagnosis. Preserve response headers and body where authorized because proxies often include an explanatory error page.

Response Common lead Next evidence
101 Upgrade accepted Post-upgrade frame/close timeline
301/302/307/308 Auth, scheme, or route redirect Location and client redirect policy
400 Missing/invalid upgrade headers or backend parser Client-to-proxy and proxy-to-backend headers
401/403 Authentication or origin policy Token/cookie/origin logs
404 Wrong path or upstream routing Host/path and proxy route
426 Upgrade required/version mismatch Protocol/version expectations
502/503/504 Proxy/backend health or timeout Upstream connection and logs

A 200 OK often means the request reached an ordinary HTTP route instead of the WebSocket endpoint, or the proxy failed to forward upgrade semantics.

Header-by-header validation

For HTTP/1.1 WebSocket, verify method/path/Host, Upgrade: websocket, Connection token containing Upgrade, Sec-WebSocket-Version: 13, client key, server accept, cookies/auth, and Origin where policy applies. Header names are case-insensitive, but intermediaries can mishandle token lists or hop-by-hop headers.

The Sec-WebSocket-Accept value is derived from the client key; when headers are visible, confirm the response corresponds to the same request. A 101 for another multiplexed/proxied request should not be matched by timing alone.

Is WebSocket Upgrade valid over HTTP/2?

Traditional Connection: Upgrade is HTTP/1.1 behavior. WebSocket over HTTP/2 uses extended CONNECT when supported. A client may negotiate h2 through ALPN while the proxy/backend expects HTTP/1.1 upgrade. Record ALPN and each proxy leg’s protocol.

Why does it work directly but fail through nginx or another proxy?

The proxy may not forward hop-by-hop headers, may route the path to the wrong upstream, may buffer or time out, or may speak a different protocol to the backend. Capture/log client-proxy and proxy-backend legs separately.

Secure WebSocket boundary

For wss://, validate DNS, TCP, SNI, certificate, ALPN, and TLS completion before HTTP. Without authorized TLS keys, do not claim header values or status code from encrypted sizes. Use client/proxy logs and state exactly which source supplied the HTTP outcome.

If TLS closes after ClientHello, follow the TLS handshake failure workflow. If TCP RST appears, use the TCP reset guide.

Post-upgrade frame and close analysis

After 101, preserve both directions, Ping/Pong, Close frames where decoded, TCP FIN/RST, retransmissions, and receive windows. A WebSocket Close frame carries a code/reason before graceful TCP teardown. A TCP RST without Close is a different lifecycle.

Post-upgrade pattern Lead
101 then immediate WebSocket Close Application/auth/protocol policy
101 then RST Process/proxy abort or invalid frame
Fixed quiet interval then FIN/RST Idle timeout
Ping sent, no Pong, then reconnect Backend/app/path liveness
Retransmissions before close Transport issue may trigger timeout
Client closes after malformed frame Client protocol validation logs

Which close codes matter?

Codes such as normal closure, going away, protocol error, unsupported data, policy violation, message too big, and internal error narrow application ownership when frames can be decoded. Keep the code and sender, and correlate with endpoint logs.

Idle timeout and heartbeat

Measure from last qualifying traffic to close across several connections. Compare WebSocket Ping/Pong with TCP keepalive and proxy read/send timeouts. A heartbeat must occur with margin before the narrowest infrastructure timer.

Use the TCP keepalive and idle timeout guide for silent drops and fixed intervals. Do not simply increase all timers; validate lifecycle and resource cost.

Proxy-leg matrix

Client→proxy Proxy→backend Interpretation
Correct Upgrade Missing Upgrade Proxy forwarding/config issue
101 received Backend 101 Upgrade succeeds end to end
502 returned Backend connect reset/timeout Upstream health/path
Client RST Backend remains open Client-side abort
Frontend alive Backend idle expired Proxy pool/lifecycle issue

Correlate legs with request ID, path, Host, and timestamp rather than assuming packet tuples match.

Regression checklist

Test ws:// and wss:// only where supported, intended Host/path, authentication expiry, Origin policy, HTTP/1.1 and HTTP/2 behavior, payload/message-size limits, idle heartbeat, proxy deploy/drain, and reconnect. Verify no duplicate application actions after reconnect.

Require the expected 101, bidirectional message exchange, graceful close behavior, and survival beyond the former idle boundary. Test through the real proxy chain, not only direct backend.

Actionable handoff

Include URL scheme/host/path, DNS result, capture points and proxy legs, TLS SNI/ALPN, visible request/response headers, status, 101 time, first data frame, close code or FIN/RST direction, idle interval, heartbeat behavior, and matching proxy/backend logs.

Preserve DNS through upgrade and the post-upgrade failure. Use capture scope help and the packet preparation workflow. Cookies, URLs, Origin, tokens, and messages require privacy review.

Direct answer: why does a WebSocket return 200 instead of 101?

Usually the request reached an ordinary HTTP handler or an intermediary removed/mishandled upgrade headers. Compare the client request with the proxy-to-backend request and confirm the intended path supports WebSocket.

Worked proxy diagnosis

Assume the browser sends a correct GET /live with Upgrade and Connection headers to a TLS-terminating proxy. The proxy-to-backend request is visible in an authorized decrypted lab capture but lacks both hop-by-hop headers. The backend returns 200 with the normal HTML page, and the proxy forwards it. This proves the browser reached the proxy and the backend route worked as HTTP; the upgrade semantics were lost on the proxy leg.

After correcting configuration, require:

  1. Client request contains expected host/path/upgrade headers.
  2. Proxy forwards the required semantics to the intended upstream.
  3. Backend and client receive matching 101 handshake.
  4. One bidirectional WebSocket message succeeds.
  5. Ping/Pong and idle behavior survive beyond the former boundary.
  6. Graceful Close or documented reconnect occurs during deploy.

What if the response is 403?

Inspect authentication, cookies, token expiry, Origin, CSRF/gateway policy, and whether the WebSocket path has a different authorization rule from normal HTTP. Redact credentials in shared artifacts while preserving status and correlation.

What if the response is 502?

The frontend proxy could not obtain a usable upstream result. Check backend DNS/connect/TLS, health, route, timeout, and whether the upstream expects cleartext HTTP/1.1 or TLS. A 502 does not prove the WebSocket headers were wrong.

Can message size break a connection after upgrade?

Yes. Client, proxy, or backend limits can reject frames/messages or close with a size-related code. Record frame/message size, fragmentation, close sender/code, and configured limits. Also inspect TCP segmentation independently; one WebSocket message need not equal one TCP packet.

Browser and non-browser client differences

Browsers set and restrict certain headers and apply Origin/cookie/security rules. Native clients may use custom headers, proxies, certificate stores, or HTTP stacks. Test the actual client class and version rather than assuming a CLI success reproduces browser behavior.

Monitoring fields that prevent vague alerts

Track upgrade attempts by route, response status, 101 rate, handshake latency, connection duration, close code/sender where available, heartbeat timeout, reconnect rate, backend selection, and client version. Separate “upgrade rejected” from “established then dropped.”

A final support answer should identify the phase: TLS never completed, HTTP returned non-101, proxy lost headers, 101 succeeded but the connection timed out, or application closed a valid WebSocket. Each phase has a different owner.

<!-- multilingual-related-reading:start -->

Related guides

Continue with the same-language pages below. They cover adjacent stages without changing the canonical owner of this topic:

<!-- multilingual-related-reading:end -->