# 101 Switching Protocols

> The server is switching to a different protocol as requested by the client. Learn about WebSocket upgrades and protocol negotiation.

Source: https://howhttpworks.com/status-codes/101
Last reviewed: 2026-10-04

> **TL;DR:** Server agrees to switch protocols (usually HTTP to WebSocket). Connection changes to new protocol after this response.

## What is 101 Switching Protocols?

A **101 Switching Protocols** status code means the server agrees to switch to a different protocol that the client requested via the `Upgrade` header. Think of it like switching from talking on the phone to having a video call—you're changing the communication method mid-conversation.

This is most commonly used to upgrade from HTTP to WebSocket for real-time bidirectional communication.

## When Does This Happen?

You'll see a 101 Switching Protocols response in these common situations:

**1. WebSocket Connection Upgrade**

```text
Client wants to establish WebSocket for real-time chat
HTTP → WebSocket
```

**2. HTTP/2 Upgrade (Less Common)**

```text
Client requests HTTP/2 over cleartext
HTTP/1.1 → HTTP/2
```

**3. Custom Protocol Upgrade**

```text
Application-specific protocol negotiation
HTTP → Custom bidirectional protocol
```

## Example Responses

**WebSocket Upgrade Request:**

```http
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
```

**101 Switching Protocols Response:**

```http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
```

## Real-World Example

Imagine you're building a real-time chat application:

**Client Initiates WebSocket Upgrade:**

```http
GET /ws/chat HTTP/1.1
Host: chat.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: https://example.com
```

**Server Accepts Upgrade:**

```http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
```

After this exchange, the connection is now a WebSocket. Both client and server can send messages at any time without the request/response pattern of HTTP.

## 101 vs Other Status Codes

| Code    | Meaning             | Protocol Change        | Use Case                      |
| ------- | ------------------- | ---------------------- | ----------------------------- |
| **101** | Switching protocols | Yes                    | WebSocket, protocol upgrades  |
| **100** | Continue            | No                     | Large request body validation |
| **200** | OK                  | No                     | Standard successful response  |
| **426** | Upgrade Required    | No (server demands it) | Force protocol upgrade        |

## Important Characteristics

**Permanent Protocol Switch:**

```http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
```

After 101, the connection no longer uses HTTP. All future data follows the new protocol.

**Must Include Upgrade Header:**

```http
# Client MUST send:
Upgrade: websocket
Connection: Upgrade

# Server MUST echo:
Upgrade: websocket
Connection: Upgrade
```

**No Response Body:**
The 101 response has no body. After the headers, the connection immediately switches to the new protocol.

## Common Mistakes

**❌ Using 101 without Upgrade header**

```http
HTTP/1.1 101 Switching Protocols
# Missing Upgrade header!
```

**❌ Sending body with 101 response**

```http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

{"message": "WebSocket ready"}  ← Wrong! No body allowed
```

**❌ Not validating WebSocket handshake**

```javascript
// ❌ Bad: Accept any upgrade request
app.get('/ws', (req, res) => {
  res.status(101).end()
})
```

**✅ Correct WebSocket upgrade**

```javascript
// ✅ Good: Proper WebSocket handshake
const crypto = require('crypto')

app.get('/ws', (req, res) => {
  const key = req.headers['sec-websocket-key']
  const acceptKey = crypto
    .createHash('sha1')
    .update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
    .digest('base64')

  res.writeHead(101, {
    Upgrade: 'websocket',
    Connection: 'Upgrade',
    'Sec-WebSocket-Accept': acceptKey
  })
})
```

## Getting 101 Switching Protocols right

**Validate Upgrade Requests:**

```javascript
app.get('/ws', (req, res) => {
  // Check required headers
  if (req.headers.upgrade?.toLowerCase() !== 'websocket') {
    return res.status(400).send('WebSocket upgrade required')
  }

  if (!req.headers['sec-websocket-key']) {
    return res.status(400).send('WebSocket key required')
  }

  // Proceed with upgrade...
})
```

**Use WebSocket Libraries:**

```javascript
// Use established libraries instead of manual implementation
const WebSocket = require('ws')

const wss = new WebSocket.Server({
  server: httpServer,
  path: '/ws'
})

wss.on('connection', (ws) => {
  ws.on('message', (data) => {
    console.log('received:', data)
  })

  ws.send('Welcome to WebSocket!')
})
```

**Handle Protocol Negotiation:**

```javascript
// Support multiple sub-protocols
app.get('/ws', (req, res) => {
  const requestedProtocols = req.headers['sec-websocket-protocol']?.split(',')
  const supportedProtocols = ['chat', 'notifications']

  const protocol = requestedProtocols?.find((p) => supportedProtocols.includes(p.trim()))

  if (!protocol) {
    return res.status(400).send('No supported protocol')
  }

  // Include selected protocol in response
  res.setHeader('Sec-WebSocket-Protocol', protocol.trim())
  // ... continue with upgrade
})
```

## Implementation Examples

**Node.js with ws library:**

```javascript
const http = require('http')
const WebSocket = require('ws')

const server = http.createServer()
const wss = new WebSocket.Server({ server })

wss.on('connection', (ws, req) => {
  console.log('Client connected from:', req.socket.remoteAddress)

  ws.on('message', (message) => {
    console.log('Received:', message)
    ws.send(`Echo: ${message}`)
  })

  ws.on('close', () => {
    console.log('Client disconnected')
  })
})

server.listen(3000)
```

**Python with websockets:**

```python
import asyncio
import websockets

async def handler(websocket, path):
    async for message in websocket:
        print(f"Received: {message}")
        await websocket.send(f"Echo: {message}")

start_server = websockets.serve(handler, "localhost", 3000)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
```

**Browser JavaScript:**

```javascript
const ws = new WebSocket('ws://localhost:3000/chat')

ws.onopen = () => {
  console.log('Connected')
  ws.send('Hello Server!')
}

ws.onmessage = (event) => {
  console.log('Received:', event.data)
}

ws.onerror = (error) => {
  console.error('WebSocket error:', error)
}

ws.onclose = () => {
  console.log('Disconnected')
}
```

## Try It Yourself

Visit our [request builder](https://howhttpworks.com/tools/playground) to see protocol upgrades:

1. Open browser DevTools Network tab
2. Visit a WebSocket demo page
3. Watch the initial HTTP request
4. See the 101 Switching Protocols response
5. Observe WebSocket frames in the WS tab

## Try it with curl

Send a WebSocket handshake by hand. `-N` disables output buffering, `--http1.1` avoids HTTP/2 (which has no `Upgrade`), and `-m 5` stops curl after 5 seconds because the connection stays open after the switch. The key below is the sample from RFC 6455.

```bash
curl -i -N --http1.1 -m 5 https://api.example.com/socket \
  -H 'Connection: Upgrade' \
  -H 'Upgrade: websocket' \
  -H 'Sec-WebSocket-Version: 13' \
  -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ=='
```

Example output (illustrative, not captured from a real server):

```http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
```

curl cannot speak WebSocket frames over this connection, so it prints nothing further and then times out. A `400` or `426` here means the server rejected the handshake or the endpoint is not a WebSocket route.

## Related Status Codes

- [100 Continue](https://howhttpworks.com/status-codes/100) - Interim response for large uploads
- [426 Upgrade Required](https://howhttpworks.com/status-codes/426) - Server requires protocol upgrade
- [200 OK](https://howhttpworks.com/status-codes/200) - Standard successful HTTP response

## Related Headers

- Upgrade - Specifies protocol to switch to
- Connection - Must be "Upgrade" for protocol switching
- Sec-WebSocket-Key - Client's WebSocket handshake key
- Sec-WebSocket-Accept - Server's WebSocket handshake response

See also the comparison [SSE vs WebSockets vs long polling](https://howhttpworks.com/compare/sse-vs-websockets): choosing a realtime transport, with proxy buffering and HTTP/2 caveats.
