Request header
> Sec-Websocket-Key: dGhlIHNhbXBsZSBub25jZQ==Sec-WebSocket-Key
Learn how the Sec-WebSocket-Key header provides a random key for WebSocket handshake validation. Understand the upgrade process and security implications.
- Direction
- Request
- Category
- WebSocket
- Spec
- RFC 6455 §4.1
- JS can set it
- No: forbidden header name
On this page
TL;DR:
Sec-WebSocket-Keyis a random 16-byte value, base64-encoded, that the client sends when opening a WebSocket. The server hashes it with a fixed GUID and sends the result back inSec-WebSocket-Accept, proving it understood the upgrade. It’s not a password or an encryption key, and browsers set it for you.
What The Header Is Doing
This example HTTP/1.1 request uses the sample key from RFC 6455:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Real clients pick a new random value for every connection. Decoded from base64, it has to be exactly 16 bytes, as required by RFC 6455 §§4.1–4.2.1.
Because the accept value depends on this exact key, an unrelated HTTP response can’t satisfy the handshake by accident. That’s all it does. It doesn’t authenticate anyone or tell you the peer is trustworthy; TLS and your application’s own authentication handle those jobs.
The Important Practical Consequence
In the browser, you open a socket with new WebSocket(url); you can’t build the upgrade yourself with fetch(). The browser generates the key. The Fetch Standard reserves headers beginning with Sec-, and the WebSockets Standard defines handshake generation.
What Server Code Has To Do
Validate the whole opening request before accepting it. To compute the accept value, append the public GUID 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 to the base64 text, not to the decoded 16 bytes, then SHA-1 hash and base64-encode the result.
Generate a diagnostic key in Node:
node --input-type=module -e 'import { randomBytes } from "node:crypto"; console.log(randomBytes(16).toString("base64"))'
That only prints a value; no request goes anywhere. A real client also has to validate the upgrade response and handle framing, masking, and closing the connection.
What To Check During Debugging
Look at the request and response side by side. Check that the key decodes to 16 bytes and that the response has status 101, Upgrade: websocket, Connection: Upgrade, and the matching accept value. If the accept value is missing or wrong, the client has to fail the handshake under RFC 6455 §4.1.
Generate a fresh key per connection; the RFC sample is for testing only. And if a proxy rewrites the key, the server’s answer won’t match what the client expects, so the handshake fails.
For a captured key, check the decoded length in Node:
node -e 'console.log(Buffer.from("dGhlIHNhbXBsZSBub25jZQ==", "base64").length)'
The RFC sample prints 16. That’s a sanity check rather than full validation: Node’s base64 decoder accepts some inputs the WebSocket grammar doesn’t allow. Leave real handshake validation to your WebSocket library.
Related Headers
Frequently asked questions
What is Sec-WebSocket-Key?
It is a random 16-byte value, base64-encoded, that the client generates fresh for every WebSocket opening handshake. The server uses it to prove it understood the upgrade request.
How does the server respond to Sec-WebSocket-Key?
The server appends the fixed WebSocket GUID to the base64 key text, takes the SHA-1 hash, and returns it base64-encoded in Sec-WebSocket-Accept. That proves the handshake worked; it says nothing about who the user is.
Why is Sec-WebSocket-Key needed?
It lets the client confirm that the server really processed its WebSocket handshake, so an ordinary HTTP response, say from a cache or a server that knows nothing about WebSockets, can never be mistaken for a successful upgrade.
Can I set Sec-WebSocket-Key manually?
Not in a browser. The WebSocket API generates it for you, and JavaScript has no way to set it.
Sources
Related
Accept Header
Learn how the Accept header tells servers which content types (JSON, HTML, XML) your client can handle. Master content negotiation and quality values.
Accept-Language Header
Learn how the Accept-Language header tells servers which languages your client prefers for localized content. Understand language tags and quality values.
Expect Header: 100-continue, curl Delays and 417
Expect: 100-continue asks the server to approve a request before the body is sent. Why curl adds it past 1 MB, the 1s delay, -H "Expect:", and 417.
Forwarded
Learn how the Forwarded header preserves original client information (IP, protocol, host) that would otherwise be lost when requests pass through proxies.