- Home
- HTTP Headers
- Upgrade Header
Header
Upgrade Header
Learn how the Upgrade header requests protocol upgrades to WebSocket, HTTP/2, or other protocols on the same TCP connection. Understand upgrade negotiation.
TL;DR: Requests switching to a different protocol on the same connection. Most commonly used to upgrade HTTP to WebSocket for real-time bidirectional communication.
What is Upgrade?
The Upgrade header allows clients and servers to negotiate switching from one protocol to another on the same connection. It’s most commonly used for upgrading from HTTP/1.1 to WebSocket, but can also be used for HTTP/2 or other protocols.
Think of it as saying “we’re currently speaking HTTP, but can we switch to WebSocket for real-time bidirectional communication?”
How Upgrade Works
Client requests protocol upgrade:
GET /chat HTTP/1.1
Host: example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
```text
**Server accepts upgrade:**
```http
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
[WebSocket communication begins]
Syntax
Upgrade: <protocol-name>[/<protocol-version>]
Upgrade: <protocol1>, <protocol2>, <protocol3>
```text
Must be used with `Connection: Upgrade` header.
### Protocol Names
- **websocket** - WebSocket protocol
- **h2c** - HTTP/2 over cleartext (non-TLS)
- **HTTP/2.0** - HTTP/2 protocol
- **TLS/1.3** - TLS version upgrade
## WebSocket Upgrade
### Client Request
```http
GET /socket HTTP/1.1
Host: example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Origin: https://example.com
Required headers for WebSocket:
- Upgrade: websocket
- Connection: Upgrade
- Sec-WebSocket-Version: 13
- Sec-WebSocket-Key:
base64-encoded key
Server Response (Success)
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
```text
Server echoes Upgrade headers and switches to WebSocket.
### Server Response (Rejection)
```http
HTTP/1.1 400 Bad Request
Content-Type: text/plain
WebSocket upgrade failed: Invalid Sec-WebSocket-Key
Server rejects upgrade and continues with HTTP.
HTTP/2 Upgrade
HTTP/2 over Cleartext (h2c)
GET / HTTP/1.1
Host: example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
```text
Client proposes HTTP/2 upgrade.
### Server Accepts
```http
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c
[HTTP/2 communication begins]
Server Rejects
HTTP/1.1 200 OK
Content-Type: text/html
<!DOCTYPE html>
<html>...
```http
Server continues with HTTP/1.1.
## Real-World Scenarios
### Chat Application
```http
# Initial HTTP request
GET /api/chat/room/123 HTTP/1.1
Host: chat.example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Protocol: chat-protocol-v1
# Server upgrades to WebSocket
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat-protocol-v1
# Now bidirectional WebSocket communication
Real-Time Dashboard
GET /api/live-metrics HTTP/1.1
Host: dashboard.example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
```http
Upgrades to WebSocket for live data streaming.
### Multiplayer Game
```http
GET /game/session/456 HTTP/1.1
Host: game.example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: ZWFjaCBub25jZSBpcyB1bmlxdWU=
Sec-WebSocket-Protocol: game-state-sync
Low-latency game state synchronization.
Stock Ticker
GET /api/stocks/stream HTTP/1.1
Host: trading.example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: c3RvY2sgdGlja2VyIGtleQ==
```javascript
Real-time stock price updates.
## Server Implementation
### Node.js (ws library)
```javascript
const http = require('http')
const WebSocket = require('ws')
const express = require('express')
const app = express()
const server = http.createServer(app)
const wss = new WebSocket.Server({ noServer: true })
// Regular HTTP routes
app.get('/api/data', (req, res) => {
res.json({ data: 'example' })
})
// Handle upgrade requests
server.on('upgrade', (request, socket, head) => {
// Verify upgrade request
if (request.headers['upgrade'] !== 'websocket') {
socket.destroy()
return
}
// Authentication/authorization check
const token = new URL(request.url, 'http://localhost').searchParams.get('token')
if (!isValidToken(token)) {
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
socket.destroy()
return
}
// Accept WebSocket upgrade
wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit('connection', ws, request)
})
})
// WebSocket connection handler
wss.on('connection', (ws, request) => {
console.log('WebSocket connection established')
ws.on('message', (message) => {
console.log('Received:', message)
ws.send(`Echo: ${message}`)
})
ws.on('close', () => {
console.log('WebSocket connection closed')
})
})
server.listen(3000)
Manual WebSocket Upgrade
const crypto = require('crypto')
const http = require('http')
const server = http.createServer((req, res) => {
res.writeHead(200)
res.end('HTTP endpoint')
})
server.on('upgrade', (req, socket, head) => {
// Verify WebSocket upgrade headers
if (req.headers['upgrade'] !== 'websocket' || !req.headers['sec-websocket-key']) {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n')
return
}
// Generate Sec-WebSocket-Accept
const key = req.headers['sec-websocket-key']
const hash = crypto
.createHash('sha1')
.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
.digest('base64')
// Send 101 Switching Protocols
const headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
`Sec-WebSocket-Accept: ${hash}`,
'\r\n'
]
socket.write(headers.join('\r\n'))
// Now in WebSocket mode
socket.on('data', (data) => {
// Handle WebSocket frames
console.log('WebSocket data received')
})
})
server.listen(3000)
```javascript
### Python (websockets)
```python
import asyncio
import websockets
from aiohttp import web
# WebSocket handler
async def websocket_handler(request):
ws = web.WebSocketResponse()
# Check if upgrade is possible
if not ws.can_prepare(request):
return web.Response(text='WebSocket upgrade failed', status=400)
# Perform upgrade
await ws.prepare(request)
# Handle messages
async for msg in ws:
if msg.type == web.WSMsgType.TEXT:
await ws.send_str(f'Echo: {msg.data}')
elif msg.type == web.WSMsgType.ERROR:
print(f'WebSocket error: {ws.exception()}')
return ws
# HTTP handler
async def http_handler(request):
return web.Response(text='HTTP endpoint')
# Setup routes
app = web.Application()
app.router.add_get('/ws', websocket_handler)
app.router.add_get('/', http_handler)
web.run_app(app, port=3000)
Go (gorilla/websocket)
package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
// Validate origin
return true
},
}
func websocketHandler(w http.ResponseWriter, r *http.Request) {
// Upgrade HTTP connection to WebSocket
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("Upgrade error:", err)
return
}
defer conn.Close()
// Handle WebSocket messages
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println("Read error:", err)
break
}
log.Printf("Received: %s", message)
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println("Write error:", err)
break
}
}
}
func httpHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("HTTP endpoint"))
}
func main() {
http.HandleFunc("/ws", websocketHandler)
http.HandleFunc("/", httpHandler)
log.Println("Server starting on :3000")
log.Fatal(http.ListenAndServe(":3000", nil))
}
```javascript
## Client Implementation
### JavaScript (Browser)
```javascript
// WebSocket constructor handles upgrade automatically
const socket = new WebSocket('wss://example.com/socket')
socket.addEventListener('open', (event) => {
console.log('WebSocket connected')
socket.send('Hello Server!')
})
socket.addEventListener('message', (event) => {
console.log('Message from server:', event.data)
})
socket.addEventListener('error', (event) => {
console.error('WebSocket error:', event)
})
socket.addEventListener('close', (event) => {
console.log('WebSocket closed:', event.code, event.reason)
})
JavaScript (Node.js)
const WebSocket = require('ws')
const ws = new WebSocket('ws://localhost:3000/socket', {
headers: {
Authorization: 'Bearer token123'
}
})
ws.on('open', () => {
console.log('Connected')
ws.send('Hello')
})
ws.on('message', (data) => {
console.log('Received:', data.toString())
})
ws.on('error', (error) => {
console.error('Error:', error)
})
ws.on('close', () => {
console.log('Connection closed')
})
```javascript
### Python (websockets)
```python
import asyncio
import websockets
async def connect():
uri = "ws://localhost:3000/socket"
async with websockets.connect(uri) as websocket:
await websocket.send("Hello Server")
response = await websocket.recv()
print(f"Received: {response}")
asyncio.run(connect())
cURL (Request Upgrade)
# Request WebSocket upgrade (won't complete handshake)
curl -i -N \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" \
http://localhost:3000/socket
# Use websocat for full WebSocket support
websocat ws://localhost:3000/socket
```text
## Security Considerations
### Validate Upgrade Requests
```javascript
server.on('upgrade', (request, socket, head) => {
// Check upgrade header
if (request.headers['upgrade'] !== 'websocket') {
socket.destroy()
return
}
// Verify WebSocket version
if (request.headers['sec-websocket-version'] !== '13') {
socket.write('HTTP/1.1 400 Bad Request\r\n\r\n')
socket.destroy()
return
}
// Verify Sec-WebSocket-Key exists
if (!request.headers['sec-websocket-key']) {
socket.write('HTTP/1.1 400 Bad Request\r\n\r\n')
socket.destroy()
return
}
// Proceed with upgrade
handleUpgrade(request, socket, head)
})
Authenticate Before Upgrading
server.on('upgrade', async (request, socket, head) => {
// Extract authentication token
const token = new URL(request.url, 'http://localhost').searchParams.get('token')
try {
// Verify token
const user = await verifyToken(token)
// Attach user to request
request.user = user
// Proceed with upgrade
wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit('connection', ws, request)
})
} catch (error) {
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
socket.destroy()
}
})
```javascript
### Validate Origin
```javascript
const upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
origin := r.Header.Get("Origin")
allowedOrigins := []string{
"https://example.com",
"https://www.example.com",
}
for _, allowed := range allowedOrigins {
if origin == allowed {
return true
}
}
return false
},
}
Rate Limiting
const rateLimiter = new Map()
server.on('upgrade', (request, socket, head) => {
const ip = request.socket.remoteAddress
const now = Date.now()
// Check rate limit
if (rateLimiter.has(ip)) {
const lastConnection = rateLimiter.get(ip)
if (now - lastConnection < 1000) {
// Less than 1 second since last connection
socket.write('HTTP/1.1 429 Too Many Requests\r\n\r\n')
socket.destroy()
return
}
}
rateLimiter.set(ip, now)
// Proceed with upgrade
handleUpgrade(request, socket, head)
})
```text
## Best Practices
### 1. Always Check Connection Header
```javascript
// ✅ Verify both headers
if (
request.headers['upgrade'] === 'websocket' &&
request.headers['connection'].toLowerCase().includes('upgrade')
) {
// Proceed
}
// ❌ Only checking Upgrade
if (request.headers['upgrade'] === 'websocket') {
// Incomplete check
}
2. Handle Upgrade Failures Gracefully
server.on('upgrade', (request, socket, head) => {
try {
validateUpgrade(request)
handleUpgrade(request, socket, head)
} catch (error) {
socket.write('HTTP/1.1 400 Bad Request\r\n\r\n')
socket.destroy()
console.error('Upgrade failed:', error)
}
})
```javascript
### 3. Use TLS for Production
```javascript
// ✅ Secure WebSocket (wss://)
const socket = new WebSocket('wss://example.com/socket')
// ❌ Insecure WebSocket (ws://) - only for development
const socket = new WebSocket('ws://example.com/socket')
4. Set Reasonable Timeouts
wss.on('connection', (ws) => {
// Ping/pong to detect broken connections
let isAlive = true
ws.on('pong', () => {
isAlive = true
})
const interval = setInterval(() => {
if (!isAlive) {
ws.terminate()
return
}
isAlive = false
ws.ping()
}, 30000)
ws.on('close', () => {
clearInterval(interval)
})
})
```text
## Common Issues
### Missing Connection Header
```http
# ❌ Missing Connection: Upgrade
GET /socket HTTP/1.1
Upgrade: websocket
# ✅ Both headers present
GET /socket HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Wrong Protocol Name
# ❌ Incorrect protocol name
Upgrade: WebSocket
# ✅ Lowercase
Upgrade: websocket
```javascript
### Mixed HTTP/HTTPS and WS/WSS
```javascript
// ❌ HTTPS page trying ws:// connection (blocked by browsers)
const socket = new WebSocket('ws://example.com/socket')
// ✅ Use wss:// with HTTPS pages
const socket = new WebSocket('wss://example.com/socket')
HTTP/2 Considerations
HTTP/2 doesn’t use Upgrade header for protocol negotiation. Instead:
- ALPN (Application-Layer Protocol Negotiation) is used during TLS handshake
- Clients can’t upgrade from HTTP/1.1 to HTTP/2 mid-connection over TLS
- h2c (HTTP/2 over cleartext) can use Upgrade for testing
Related Headers
- Connection - Required companion header
- Sec-WebSocket-Key - WebSocket handshake key
- Sec-WebSocket-Accept - Server handshake response
Frequently Asked Questions
What is the Upgrade header?
Upgrade requests switching to a different protocol on the same connection. Most commonly used to upgrade HTTP to WebSocket. Server responds with 101 if it agrees.
How does WebSocket upgrade work?
Client sends Upgrade: websocket with Connection: Upgrade. Server responds 101 Switching Protocols with same headers. The connection then uses WebSocket protocol.
Can Upgrade be used for HTTP/2?
HTTP/2 upgrade via Upgrade header is possible but rarely used. Most HTTP/2 connections use ALPN during TLS handshake instead.
What happens if server ignores Upgrade?
Server can ignore Upgrade and respond normally with HTTP. The client should handle both upgraded and non-upgraded responses gracefully.