HTTP status code · Informational (1xx)
101 Switching Protocols
101 Switching Protocols means the server accepted the client's Upgrade request and the connection now speaks a different protocol. In practice you see it every time a browser opens a WebSocket: the 101 is the successful end of the handshake, not an error.
| Class | 1xx, Informational |
|---|---|
| Defined in | RFC 9110 §15.2.2 |
| Cacheable by default | No |
| Safe to retry | Not applicable; after a 101 the connection speaks the new protocol |
| Relevant headers |
|
What 101 means
RFC 9110, section 15.2.2, requires the server to include an Upgrade header naming the protocol that takes over. For a WebSocket (RFC 6455) the browser sends Upgrade: websocket, Connection: Upgrade and a random Sec-WebSocket-Key; the server answers 101 with Sec-WebSocket-Accept, a hash of that key. After the blank line that ends the 101, no more HTTP travels on that TCP connection.
The mechanism belongs to HTTP/1.1. HTTP/2 does not use 101 at all (RFC 9113, section 8.6), and WebSockets over HTTP/2 use an extended CONNECT request instead (RFC 8441). That is why a reverse proxy has to speak HTTP/1.1 to the backend for the upgrade to get through.
When to use it
- Let your WebSocket library send it; hand-writing the handshake means computing Sec-WebSocket-Accept yourself and is easy to get wrong.
- Put the WebSocket endpoint on its own path (/ws) so proxy rules for the upgrade apply only there.
Common causes
If you are visiting the site
- A chat, live score or dashboard never updates: a corporate proxy, VPN or antivirus is blocking WebSocket upgrades on your network.
If you run the server
- The handshake returns 200, 400 or 404 instead of 101: nginx or another proxy dropped the Upgrade and Connection headers, which are hop-by-hop and not forwarded by default.
- The socket opens with 101 and dies after exactly 60 seconds of silence: nginx closes idle proxied connections after proxy_read_timeout, which defaults to 60s.
How to fix it
If you are visiting the site
- Try another network or disable the VPN or HTTPS inspection feature of your antivirus, then reload the page.
If you run the server
- In nginx set proxy_http_version 1.1 and pass Upgrade and Connection explicitly, as in the example below.
- Send WebSocket ping frames every 20 to 30 seconds, or raise proxy_read_timeout for the /ws location.
How to send 101
Next.js route handlers cannot take over the socket, so they cannot answer an upgrade; run the WebSocket server as a separate process or a custom server.
import { WebSocketServer } from 'ws';
const server = app.listen(3000);
const wss = new WebSocketServer({ noServer: true });
// ws writes "101 Switching Protocols" and Sec-WebSocket-Accept for you
server.on('upgrade', (req, socket, head) => {
if (req.url !== '/ws') return socket.destroy();
wss.handleUpgrade(req, socket, head, (ws) => {
ws.send('connected');
});
});// github.com/gorilla/websocket
var upgrader = websocket.Upgrader{}
mux.HandleFunc("GET /ws", func(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil) // sends 101 Switching Protocols
if err != nil {
return // Upgrade already wrote an HTTP error response
}
defer conn.Close()
conn.WriteMessage(websocket.TextMessage, []byte("connected"))
})from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def ws_endpoint(websocket: WebSocket):
await websocket.accept() # the server sends 101 Switching Protocols
await websocket.send_text("connected")location /ws {
proxy_pass http://app;
proxy_http_version 1.1; # 101 only exists in HTTP/1.1
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 1h; # default 60s kills idle sockets
}Commonly confused with
- 101 vs 426
- 426 Upgrade Required is the server demanding a protocol change; 101 is the server agreeing to one the client asked for.
- 101 vs 200
- A 200 to a WebSocket handshake means something ignored the Upgrade header and served a normal page, so the socket fails.
Frequently asked questions
- Is status 101 an error in the browser Network tab?
- No. A WebSocket row with status 101 means the handshake succeeded. The messages that follow appear under that row, in the Messages or Response tab depending on the browser.
- Why does my WebSocket get 200 or 400 instead of 101?
- Usually a reverse proxy is not forwarding the Upgrade and Connection headers, so the backend sees a plain GET. In nginx add proxy_http_version 1.1 and the two proxy_set_header lines for Upgrade and Connection.
- Does HTTP/2 use 101 Switching Protocols?
- No. RFC 9113 says HTTP/2 does not use 101. WebSockets over HTTP/2 are opened with an extended CONNECT request defined in RFC 8441, and many servers simply keep WebSockets on HTTP/1.1.
Last reviewed by Arielton Oberek.