WebSocket HTTP Upgrade — Complete Guide
In this tutorial, you will learn about Websocket HTTP Upgrade. We cover key concepts, practical examples, and best practices to help you master this topic.
The WebSocket connection begins with an HTTP upgrade handshake. This standard mechanism allows WebSocket to work through existing HTTP infrastructure while switching to a more efficient protocol.
What You'll Learn
- HTTP upgrade request structure
- Key handshake headers
- Server response format
- Security during the handshake
- Troubleshooting handshake failures
Why It Matters
Understanding the handshake is essential for debugging connection issues, implementing custom servers, and configuring proxies that handle WebSocket traffic.
Real-World Use
A cloud provider's load balancer must recognize WebSocket upgrade requests and route them correctly. Understanding handshake headers allows ops teams to configure proxies that do not drop or time out WebSocket connections.
Flow Chart
sequenceDiagram
participant C as Client
participant S as Server
C->>S: GET /ws HTTP/1.1
C->>S: Upgrade: websocket
C->>S: Connection: Upgrade
C->>S: Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
C->>S: Sec-WebSocket-Version: 13
S->>C: HTTP/1.1 101 Switching Protocols
S->>C: Upgrade: websocket
S->>C: Connection: Upgrade
S->>C: Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Note over C,S: Full-duplex communication begins
Code Examples
Example 1: Manual Upgrade Request
const http = require('http');
const crypto = require('crypto');
const key = crypto.randomBytes(16).toString('base64');
const options = {
hostname: 'echo.example.com',
port: 80,
path: '/ws',
headers: {
'Upgrade': 'websocket',
'Connection': 'Upgrade',
'Sec-WebSocket-Key': key,
'Sec-WebSocket-Version': '13',
},
};
const req = http.request(options);
req.end();
req.on('upgrade', (res, socket) => {
const acceptKey = res.headers['sec-websocket-accept'];
const expectedAccept = crypto
.createHash('sha1')
.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
.digest('base64');
if (acceptKey === expectedAccept) {
console.log('WebSocket handshake successful');
// Now use socket for WebSocket communication
}
});
Expected output: Client performs manual WebSocket handshake and verifies the server's accept key.
Example 2: Server-Side Handshake Verification
const http = require('http');
const crypto = require('crypto');
const server = http.createServer((req, res) => {
res.writeHead(400);
res.end();
});
server.on('upgrade', (req, socket, head) => {
const key = req.headers['sec-websocket-key'];
const version = req.headers['sec-websocket-version'];
if (version !== '13') {
socket.write('HTTP/1.1 426 Upgrade Required\r\n\r\n');
socket.destroy();
return;
}
const acceptKey = crypto
.createHash('sha1')
.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
.digest('base64');
socket.write(
'HTTP/1.1 101 Switching Protocols\r\n' +
'Upgrade: websocket\r\n' +
'Connection: Upgrade\r\n' +
`Sec-WebSocket-Accept: ${acceptKey}\r\n` +
'\r\n'
);
console.log('WebSocket connection established');
// WebSocket communication follows
});
server.listen(8080);
Expected output: Server validates the WebSocket version, computes the accept key, and completes the handshake.
Example 3: Handshake with Custom Headers
// Client with custom headers
const ws = new WebSocket('wss://api.example.com/ws', {
headers: {
'Authorization': 'Bearer token123',
'X-Client-Version': '2.1.0',
'X-Device-Id': 'device-456',
},
});
// Server extracting custom headers
server.on('upgrade', (req, socket, head) => {
const auth = req.headers['authorization'];
const clientVersion = req.headers['x-client-version'];
const deviceId = req.headers['x-device-id'];
if (!auth || !auth.startsWith('Bearer ')) {
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
socket.destroy();
return;
}
const token = auth.slice(7);
if (!validateToken(token)) {
socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
socket.destroy();
return;
}
console.log(`Client ${deviceId} v${clientVersion} connected`);
// Complete handshake...
});
Expected output: Custom headers passed during the WebSocket handshake enable authentication and client identification.
Common Mistakes
| Mistake | Explanation |
|---|---|
| Missing Connection: Upgrade header | Both Upgrade and Connection headers are required for a valid handshake |
| Wrong Sec-WebSocket-Version | Only version 13 is widely supported; older versions are obsolete |
| Ignoring Sec-WebSocket-Accept verification | Clients should verify the server's accept key to prevent hijacking |
| Forgetting the magic GUID | The server must append 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 before hashing the key |
| Not validating origin header | In production, validate the Origin header to prevent cross-origin WebSocket attacks |
Practice Questions
- What HTTP status code indicates a successful WebSocket upgrade?
- What is the purpose of the Sec-WebSocket-Key header?
- How does the server compute the Sec-WebSocket-Accept value?
- What happens if the server does not support WebSocket?
- Can custom headers be passed during the WebSocket handshake?
Challenge
Build a WebSocket server that authenticates clients during the handshake using a custom token header. Reject unauthorized connections with appropriate HTTP status codes and log all handshake attempts.
FAQ
Mini Project
Build a WebSocket handshake diagnostic tool that connects to any WebSocket endpoint, displays all handshake headers sent and received, verifies the Sec-WebSocket-Accept key, and reports any issues with the handshake Process.
What's Next
Learn about WebSocket frames and data transmission
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro