cloudflare / cloudflare/cloudflared

💡 Feature Request: CORS Support for api.trycloudflare.com/tunnel

Open
#1,634 0 comments 0 reactions 0 assignees View on GitHub
Priority: Normal Type: Feature Request
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.