cloudflare / cloudflare/cloudflared
💡 Feature Request: CORS Support for api.trycloudflare.com/tunnel
- Dominant language
- Go
- Stars
- 15.6k
- Forks
- 1.4k
- PR merge metrics
- No merged PRs in 30d
Description
## Feature Request: CORS Support for api.trycloudflare.com/tunnel
### Description
Currently, the `api.trycloudflare.com/tunnel` endpoint (used for Quick Tunnels / TryCloudflare) does not return Cross-Origin Resource Sharing (CORS) headers. This prevents developers from programmatically creating and managing ephemeral tunnels directly from browser-based environments (Web IDEs, browser consoles, or client-side admin dashboards) without using a custom proxy or a local `cloudflared` binary.
Adding `Access-Control-Allow-Origin: *` (or allowing specific origins) and supporting the `OPTIONS` preflight would allow for a new class of "Browser-to-Global" applications where a browser tab can act as a temporary origin.
### Use Case
Developers often need to share a "Virtual File System" (VFS) or a local state directly from a browser tab to the public internet for debugging or temporary sharing. With CORS support, we could:
1. Register a tunnel via `fetch`.
2. Keep the tunnel alive using a polling heartbeat or WebSocket.
3. Serve content directly from the browser's memory/VFS.
---
### Proposed Implementation Examples
#### 1. Registration (Fetch Example)
With CORS enabled, we could register a tunnel directly:
```javascript
const response = await fetch("https://api.trycloudflare.com/tunnel", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ protocol: "http" })
});
const { result } = await response.json();
console.log(`Tunnel assigned: https://${result.hostname}`);
```
#### 2. Keeping the Tunnel Alive (Polling Heartbeat)
Since browsers cannot always maintain a persistent binary socket like `cloudflared`, a polling mechanism could allow the browser to "announce" it is still active and update the served content:
```javascript
const secret = result.secret;
const tunnelUrl = `https://${result.hostname}`;
// Heartbeat to keep the route active and update content
setInterval(async () => {
await fetch(tunnelUrl, {
method: "POST",
headers: {
"X-Tunnel-Secret": secret,
"Content-Type": "text/plain"
},
body: "Current VFS State: " + new Date().toISOString(),
mode: 'cors'
});
}, 5000);
```
#### 3. Real-time Streaming (WebSocket Example)
Ideally, if the Edge allowed WebSocket upgrades with the tunnel secret, we could stream data in real-time:
```javascript
// This currently fails because native WebSockets can't send the X-Tunnel-Secret header.
// A potential fix would be allowing the secret in a query param for browser clients.
const socket = new WebSocket(`wss://region1.v2.argotunnel.com/?id=${result.id}&secret=${result.secret}`);
socket.onmessage = (event) => {
// Respond to an incoming HTTP request from the internet
const response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n
Hello from Browser!
";socket.send(response);
};
```
### Requested Changes
- Add `Access-Control-Allow-Origin` headers to `https://api.trycloudflare.com/tunnel`.
- Allow `X-Tunnel-Secret` in the `Access-Control-Allow-Headers` list.
- Consider a WebSocket-friendly handshake that doesn't require custom headers (e.g., via query parameters) for pure browser compatibility.
Contributor guide
Assessment
This issue has not been assessed yet.