Skip to content

Instantly share code, notes, and snippets.

@htp
Last active May 28, 2025 02:24
Show Gist options
  • Save htp/fbce19069187ec1cc486b594104f01d0 to your computer and use it in GitHub Desktop.
Save htp/fbce19069187ec1cc486b594104f01d0 to your computer and use it in GitHub Desktop.
Test a WebSocket using curl.
curl --include \
--no-buffer \
--header "Connection: Upgrade" \
--header "Upgrade: websocket" \
--header "Host: example.com:80" \
--header "Origin: http://example.com:80" \
--header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
--header "Sec-WebSocket-Version: 13" \
http://example.com:80/
@marianopeck
Copy link

Thanks @vi that worked perfectly!

@tony-caffe
Copy link

I recommend adding --http1.1 to your curl command. The Connection and Upgrade headers are not valid in http/2 and curl will use http/2 if your server supports it.

I lost a few hours trying to figure out why these headers were disappearing when debugging my httpd web socket rewrite rules for the first time.

You saved my life haha. I wasted so much time thinking my move to E2EE was the issue but stupid ActionCable and AWS Target Group using HTTP/2 was the issue! 🤦

Time to look into AnyCable or something else. Thank you!

@realyukii
Copy link

I got status code 400 :"

I tried to connect to websocket server with simple setup using ws library:

const { WebSocketServer } = require('ws');

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws) {
  ws.on('error', console.error);

  ws.on('message', function message(data) {
    console.log('received: %s', data);
  });

  ws.send('something');
});

and I can't use curl to test it:

curl --include \
     --no-buffer \
     --header "Connection: Upgrade" \
     --header "Upgrade: websocket" \
     --header "Host: localhost:8080" \
     --header "Origin: http://localhost:8080" \
     --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
     --header "Sec-WebSocket-Version: 13" \
     http://localhost:8080/
HTTP/1.1 400 Bad Request
Connection: close
Content-Type: text/html
Content-Length: 43

Missing or invalid Sec-WebSocket-Key header

@realyukii
Copy link

realyukii commented May 27, 2025

browser developer tools on the network tab have copy as curl value, however it seems still no luck:

curl 'ws://localhost:8080/' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.7,id;q=0.3' -H 'Accept-Encoding: gzip, deflate, br, zstd' -H 'Sec-WebSocket-Version: 13' -H 'Origin: null' -H 'Sec-WebSocket-Extensions: permessage-deflate' -H 'Sec-WebSocket-Key: 3tMWYzcHwmObiLR3nWOgsg==' -H 'Connection: keep-alive, Upgrade' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: websocket' -H 'Sec-Fetch-Site: cross-site' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' -H 'Upgrade: websocket'
curl: (22) Refused WebSockets upgrade: 400

@realyukii
Copy link

realyukii commented May 27, 2025

okay, changing Sec-WebSocket-key with the one provided from browser works (not sure why):

curl --include \
     --no-buffer \
     --header "Connection: Upgrade" \
     --header "Upgrade: websocket" \
     --header "Host: localhost:8080" \
     --header "Origin: http://localhost:8080" \
     --header "Sec-WebSocket-Key: 3tMWYzcHwmObiLR3nWOgsg==" \
     --header "Sec-WebSocket-Version: 13" \
     http://localhost:8080/
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: YLxhg+bEzrZXCaOyyI5QkRBIdT4=

�       something

EDIT:
after reading the wikipedia, turned out it just a random 16-byte encoded with base64, on Linux, you can generate it with:

head -c 16 /dev/urandom | base64

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment