[Feature Request] tailscale: add control_websocket option for coordination servers behind CDNs / Cloudflare
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 38.2k
- Forks
- 4.6k
- Avg merge
- 19d 15h
- Merged PRs (30d)
- 1
Description
Background & Problem
Tailscale clients establish their coordination control plane session (TS2021) by making an HTTP POST /ts2021 request with non-standard upgrade
headers:
POST /ts2021 HTTP/1.1
Upgrade: tailscale-control-protocol
Connection: upgrade
X-Tailscale-Handshake: <base64>
When self-hosting a coordination server (e.g. Headscale) behind CDNs or restrictive reverse proxies (such as Cloudflare Tunnel / Cloudflare Proxy):
- RFC 6455 Violation: RFC 6455 Section 4.1 (https://datatracker.ietf.org/doc/html/rfc6455#section-4.1) mandates that the WebSocket upgrade
handshake must use the GET method. - Upgrade Header Filtering: Cloudflare and similar edge proxies strictly filter WebSocket upgrade requests. They drop non-GET upgrades and only
accept standard Upgrade: websocket. Any non-standard Upgrade: tailscale-control-protocol sent with POST is immediately rejected with an HTTP
error (e.g. 500 Internal Server Error or 405 Method Not Allowed).
This limitation is well documented across the community:
- juanfont/headscale#3060: TS2021 POST handshake fails when proxied through Cloudflare Tunnel (WebSocket implementation only accepts GET)
- juanfont/headscale#1468: Purpose of non-standard POST method in Websocket handshake?
- cloudflare/cloudflared#883: Upgrade header stripped on Websocket POST requests
- Headscale Official Docs: Reverse Proxy - Cloudflare explicitly documents
that Cloudflare Tunnel/Proxy is unsupported due to Tailscale's POST upgrade requirement.
Tailscale's repository actually already contains native WebSocket tunneling implementations (control/controlhttp/client_js.go, net/wsconn, and
controlhttpserver.acceptWebsocket), but it is only compiled in for js/wasm targets.
Proposed Solution
Add a control_websocket boolean option to TailscaleEndpointOptions:
{
"type": "tailscale",
"tag": "ts-ep",
"control_url": "https://headscale.example.com",
"control_websocket": true
}
Reference Implementation
A fully verified and tested implementation is available on my fork:
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with TailscaleEndpointOptions and the existing control/controlhttp/client_js.go, net/wsconn, and controlhttpserver.acceptWebsocket implementations. Compare the proposed behavior with the tailscale-wss branch and commit linked in the issue. Done means the tailscale endpoint accepts control_websocket in its configuration and can use the WebSocket path for coordination servers behind restrictive proxies.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- networking
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100