WebSocket 1006, 101 and Ping/Pong Explained: The Words Before the Deep Dive
▶ Watch on YouTube & subscribe to The Stack Underflow
101 Switching Protocols looks like an odd status code. It isn’t an error: it’s the last HTTP response on a WebSocket connection, and from then on the connection carries frames. This primer explains the WebSocket words, each with a memory peg, before the deep dive follows one WebSocket through Application Gateway, NGINX, Tomcat and Spring.
The one-line version: a WebSocket starts as an HTTP request and ends its HTTP life with one answer, 101. After that, everything is a frame, and a goodbye is a Close frame with a code. 1006 means no goodbye arrived.
Last verified against RFC 6455, the WHATWG WebSockets standard, nginx.org and NGINX’s source, the Spring reference, the STOMP 1.2 specification and Microsoft Learn: 6 October 2026. Scope: the classic HTTP/1.1 Upgrade path. Every claim links to its source at the bottom of the page.
How the pegs work
Every word gets a peg, a picture to hang it on. The pegs are memory aids, not how the protocol works.
The address: ws://, wss:// and where TLS ends
| Word | Memory peg | What it actually is |
|---|---|---|
| ws:// vs wss:// | Postcard vs sealed envelope | ws is unencrypted, on port 80 by default. wss runs WebSocket over TLS, on port 443 by default, and TLS is what gives the connection confidentiality and integrity. The standard says implementations should use it. |
| TLS termination | The mail room | Where the envelope is opened. Application Gateway can end TLS, and so can NGINX. Behind that point, the next leg is a separate connection: often plain, or encrypted again with a new TLS session. So wss in the browser doesn’t mean TLS all the way to Tomcat. |
The handshake: Upgrade, Connection and 101
A WebSocket starts as an ordinary HTTP request, and ends its HTTP life with one answer.
| Word | Memory peg | What it actually is |
|---|---|---|
| Opening handshake | The knock | An HTTP/1.1 GET asking the server to switch protocols. WebSocket over HTTP/2 or HTTP/3 bootstraps differently. |
| Upgrade and Connection | A one-hop sticky note | The request carries Upgrade: websocket and Connection: Upgrade. Both are hop-by-hop headers, so NGINX doesn’t pass them to the server behind it unless you set them explicitly, with proxy_set_header. |
| 101 Switching Protocols | The door opens | The server completes the handshake with 101. It isn’t an error: it’s the last HTTP response on this connection, and from then on it carries WebSocket frames. With any other status, the browser fails the connection. |
| Sec-WebSocket-Key and Sec-WebSocket-Accept | Challenge and stamped reply | The browser sends a Sec-WebSocket-Key. The server appends a fixed GUID to it, hashes that with SHA-1, base64-encodes the result, and returns it as Sec-WebSocket-Accept. That proves the server received a WebSocket handshake, so the server doesn’t accept connections that aren’t WebSocket connections. |
Frames: masking, ping/pong and heartbeats
After 101, everything is a frame.
| Word | Memory peg | What it actually is |
|---|---|---|
| Frame | Labelled parcels on a conveyor belt | The unit a WebSocket sends. Its opcode says what it is: text, binary, close, ping or pong, or a continuation of a message split across frames. The FIN bit marks the final fragment. |
| Masking | Only the browser wears a mask | A client, here the browser, must mask every frame it sends to the server, and the server must not mask its frames. Where the peg breaks: masking isn’t encryption. The masking key travels inside the frame. Masking stops a page’s script from choosing the exact bytes on the wire, which an intermediary could misread as an HTTP request. Encryption comes from TLS. |
| Ping / pong | Marco… Polo | A ping is a control frame, and whoever receives one must answer with a pong, unless it has already received a Close frame. A ping can work as a keepalive, or check that the other end still responds. The browser’s JavaScript API doesn’t expose ping and pong, though the browser itself may send them. |
| Heartbeat | The regular knock | Traffic you send on purpose, so a quiet connection isn’t closed as idle: a server ping, a STOMP heart-beat, or an application message. By default, NGINX closes a proxied WebSocket that’s quiet for 60 seconds. A heartbeat only helps if it reaches the timer you’re fighting. |
Goodbyes: close codes, 1006, and STOMP on top
| Word | Memory peg | What it actually is |
|---|---|---|
| Close code | The goodbye note | A clean goodbye is a Close frame with a code. 1000 is a normal closure. 1001 means going away, like a server going down or a browser leaving the page. 1011 means the server hit an unexpected condition. The other side answers with its own Close frame, typically echoing the code. |
| 1006 | The slammed door, with no note | Reserved: it must never be sent in a Close frame. The browser reports it when the connection closed without one: a dropped connection, a proxy timeout, or a handshake that failed. 1006 wasn’t sent by the server. It means no goodbye arrived. |
| STOMP | The sorting office on top of the line | A messaging protocol that Spring runs over WebSocket, for publish-subscribe messaging through a broker. STOMP declares heart-beats in a heart-beat header on its own CONNECT and CONNECTED messages, which travel inside WebSocket frames, and Spring’s simple broker supports them when it has a task scheduler. |
The whole map
- The address: the browser’s
wssaddress, and the mail room where TLS ends. - The handshake: Upgrade and Connection, the key and its accept, and 101.
- Frames: masked from the browser, pings and pongs, heartbeats.
- The goodbye: a close code, or 1006 when none arrived, with STOMP riding on top.
Pause & Prove
1. The pinned question
Your WebSocket handshake gets back a 101. Is something wrong? And later the browser reports close code 1006: who sent it?
Nothing is wrong: 101 Switching Protocols completes the handshake. It’s the last HTTP response on the connection; from then on it carries WebSocket frames. And nobody sent the 1006: it’s reserved and must never be sent in a Close frame. The browser reports it when the connection closed without one.
2. The Community poll
Browser-to-server WebSocket frames are masked. Is masking a form of encryption?
- Yes, that’s why it’s safe. The tempting one. Masking isn’t encryption; it stops a page’s script from choosing the exact bytes on the wire, which an intermediary could misread as an HTTP request.
- No: the key is inside the frame. ✓ The masking key travels inside the frame itself. Encryption comes from TLS, with
wss://. - Only on wss://. A client must mask every frame it sends to the server, on
ws://andwss://alike. TLS is a separate layer. - Only from server to browser. The other way round: the browser masks; the server must not mask its frames.
3. Peg drill
Say the peg for each before reading on: wss, TLS termination, Upgrade, 101, masking, ping, heartbeat, 1006.
Answers: wss is a sealed envelope, and TLS termination is the mail room that opens it. Upgrade is a sticky note that lasts one hop. 101 is the door opening. Only the browser wears a mask. Ping and pong are Marco and Polo. A heartbeat is the regular knock. And 1006 is the slammed door, with no goodbye note.
Before / after this video
- Before, if NGINX and Tomcat are new: Tomcat vs NGINX: What Each Does and Why Spring Boot May Need Both
- After: Why Your WebSocket Disconnects After 60 Seconds: Spring Boot Behind NGINX, the deep dive that uses every word on this page. Watch for the 101, the missing headers, and the 1006.
Sources
Read on 6 October 2026:
- RFC 6455, The WebSocket Protocol:
ws/wssand ports 80/443, TLS (§10.6, §11.1.2), the HTTP/1.1GETwith Upgrade and Connection (§4.1), 101, Sec-WebSocket-Accept from the key and the GUID (§1.3), opcodes and FIN (§5.2), masking (§5.1, §5.3, §10.3), ping and pong (§5.5.2), the Close handshake (§5.5.1), close codes 1000, 1001, 1011 and 1006 (§7.4.1, §7.1.5) - WHATWG WebSockets Standard: ping and pong “are not currently exposed in the API”, the browser may send them itself; a non-101 handshake fails the connection; the cases that report 1006
- nginx.org, WebSocket proxying: hop-by-hop Upgrade and Connection must be passed explicitly; closed after 60 s without data by default
- NGINX source,
src/http/ngx_http_upstream.c: on an upgraded connection, traffic in either direction restarts theproxy_read_timeoutclock - nginx.org, Configuring HTTPS servers: NGINX can end TLS
- Microsoft Learn, Application Gateway overview and TLS termination: a layer 7 proxy that can end TLS and opens a separate backend connection
- Spring Framework, WebSocket: STOMP as a sub-protocol over WebSocket for publish-subscribe messaging
- Spring Framework, Simple broker: heartbeats when configured with a task scheduler
- STOMP 1.2 specification, Heart-beating: the
heart-beatheader on CONNECT and CONNECTED - RFC 8441 (WebSocket over HTTP/2) and RFC 9220 (over HTTP/3): a different bootstrap
Change notes
- 7 Oct 2026: first published.
Not affiliated with or endorsed by F5, the nginx project, the Apache Software Foundation or Microsoft. NGINX is a trademark of F5, Inc. Found a mistake? Tell us in the video’s comments and we’ll correct this page.
Found this useful? The deep version lives on YouTube — new breakdowns of how AI dev tools actually work, weekly.
Subscribe on YouTube →Prefer email? Get the free newsletter: one failure, traced step by step, about once a week.