// Thin relay: this Worker owns no state. // // It forwards every request to the relay server and streams the answer back, so // the panel, the database and the agent API all live on your own host. Nothing // is stored in Cloudflare except what the platform keeps in transit. // // Bindings: // var SERVER_URL https://your-server.example // secret PROXY_SECRET optional shared header the server can require export default { async fetch(request, env) { const base = (env.SERVER_URL || "").replace(/\/$/, ""); if (!base) return new Response("SERVER_URL is not configured", { status: 500 }); const url = new URL(request.url); const target = base + url.pathname + url.search; const headers = new Headers(request.headers); // Do not leak Cloudflare internals to the origin. headers.delete("cf-connecting-ip"); headers.delete("cf-ray"); headers.delete("cf-visitor"); headers.delete("x-forwarded-proto"); if (env.PROXY_SECRET) headers.set("X-Proxy-Secret", env.PROXY_SECRET); const init = { method: request.method, headers, redirect: "manual" }; if (request.method !== "GET" && request.method !== "HEAD") init.body = request.body; let upstream; try { upstream = await fetch(target, init); } catch (err) { return new Response("relay unreachable: " + (err && err.message ? err.message : err), { status: 502 }); } const response = new Response(upstream.body, { status: upstream.status, statusText: upstream.statusText, headers: upstream.headers }); response.headers.set("Cache-Control", "no-store"); return response; } };