HTTP 101 Switching Protocols is sent by the server in response to an Upgrade request header from the client, indicating that the server is switching to the protocol specified in the Upgrade header. This is the mechanism that enables WebSocket connections — the client sends an HTTP/1.1 GET with 'Upgrade: websocket' and 'Connection: Upgrade', and the server responds with 101 to confirm the protocol switch. After the 101 response, the connection is no longer HTTP; it speaks whatever protocol was negotiated.
Response includes the status code, standard headers (including Content-Type), and a small diagnostic JSON body describing the request and returned status.
Simulator URL (copy in the app after load — not a normal link):
https://httpstatus.com/api/status/101
Example request:
curl -i "https://httpstatus.com/api/status/101"The server is switching protocols as requested by the client via the Upgrade header (e.g., upgrading to WebSocket).
On this code, Inspector focuses on semantics, headers, and correctness warnings that commonly affect clients and caches.
Defined in RFC 7231 Section 6.2.2, the 101 response MUST include an Upgrade header field indicating the protocol(s) being switched to. The most common use is the WebSocket handshake (RFC 6455): the client sends 'Sec-WebSocket-Key' (a base64-encoded random value), and the server responds with 'Sec-WebSocket-Accept' (the key concatenated with a magic GUID and SHA-1 hashed). This proves the server understands WebSocket, not just any Upgrade request. HTTP/2 connections can also be initiated via 101 using 'Upgrade: h2c' for cleartext HTTP/2, though this is rare since most HTTP/2 uses ALPN during TLS negotiation. Important: 101 is a hop-by-hop status — proxies must handle the Upgrade themselves or tunnel the connection.
const WebSocket = require('ws');
const http = require('http');
const server = http.createServer();
const wss = new WebSocket.Server({ noServer: true });
server.on('upgrade', (request, socket, head) => {
// Authenticate before completing the 101 handshake
const token = new URL(request.url, 'http://localhost')
.searchParams.get('token');
if (!isValid(token)) {
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
socket.destroy();
return;
}
wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit('connection', ws, request);
});
});import asyncio
import websockets
async def handler(websocket, path):
async for message in websocket:
response = f'Echo: {message}'
await websocket.send(response)
async def main():
# Server sends 101 Switching Protocols automatically
async with websockets.serve(
handler, 'localhost', 8765,
extra_headers={'X-Server': 'MyApp'}
):
await asyncio.Future() # run forever
asyncio.run(main())@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(
WebSocketHandlerRegistry registry) {
registry.addHandler(new MyHandler(), "/ws")
.setAllowedOrigins("https://myapp.com")
.addInterceptors(new AuthHandshakeInterceptor());
}
}
public class MyHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(
WebSocketSession session, TextMessage msg) {
session.sendMessage(
new TextMessage("Echo: " + msg.getPayload()));
}
}package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return r.Header.Get("Origin") == "https://myapp.com"
},
}
func wsHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil) // sends 101
if err != nil {
log.Println("Upgrade failed:", err)
return
}
defer conn.Close()
for {
_, msg, err := conn.ReadMessage()
if err != nil { break }
conn.WriteMessage(websocket.TextMessage, msg)
}
}