cf-relay
v0.1.3
Published
Deploy a Cloudflare Workers edge relay — x-relay-target/x-relay-path header proxy with interactive account picker
Maintainers
Readme
cf-relay
Deploy a tiny Cloudflare Workers edge relay with one command.
The deployed Worker reads two request headers — x-relay-target and x-relay-path — and forwards your request to that target. Your real IP is replaced by Cloudflare's edge IPs (300+ cities). The relay protocol is identical to the Vercel edge-relay function used by 9router, so the deployed URL drops straight into existing relay consumers without any protocol changes.
┌──────────┐ x-relay-target: https://api.openai.com ┌─────────────┐ ┌─────────┐
│ Client │ ──────────────────────────────────────────▶ │ CF Worker │ ─▶ │ OpenAI │
│ (you) │ x-relay-path: /v1/chat/completions │ (edge IPs) │ │ │
└──────────┘ └─────────────┘ └─────────┘- Free tier: 100,000 requests/day, 10ms CPU per request.
- IP diversity: all Cloudflare egress (AS13335), rotates by edge colo. For more diversity, deploy the same Worker on multiple Cloudflare accounts.
- No region pinning: Workers don't let you choose outbound region. Use a real geo proxy if you need IP-by-country.
Install
No install needed — run it directly with npx:
npx cf-relayOr install globally if you want it on PATH permanently:
npm i -g cf-relay
cf-relayRequires Node.js 20+.
One-time Cloudflare setup
You need (1) a free Cloudflare account and (2) an API token. Total time: ~2 minutes.
1. Claim your workers.dev subdomain
The very first time you use Workers on an account, you need to claim a subdomain. Open this once:
https://dash.cloudflare.com/?to=/:account/workers-and-pages
Just visiting the page provisions a subdomain like your-name.workers.dev. You don't have to click anything.
2. Create an API token
- Go to https://dash.cloudflare.com/profile/api-tokens
- Click Create Token
- Pick the template Edit Cloudflare Workers
- On the permissions screen:
- Account Resources → leave as Include → All accounts (or narrow to one)
- Zone Resources → leave default (not used by this CLI but the template includes it)
- Client IP / TTL → optional
- Continue → Create Token — copy the value immediately; Cloudflare shows it only once.
The minimum permissions the CLI actually uses (in case you build a custom token):
| Permission | Why |
|---|---|
| Account → Workers Scripts → Edit | Upload the script, enable workers.dev URL |
| User → User Details → Read | /user/tokens/verify |
| User → Memberships → Read | List accounts in the picker |
Usage
Run the CLI — it prompts for everything else:
npx cf-relay? Cloudflare API token (Workers Scripts:Edit): **********
? Save token to /home/you/.config/cf-relay/config.json? Yes
Verifying token... ok
Account: My Account (7f3c084c…)
? Worker script name: relay-l9k2m1
Uploading worker "relay-l9k2m1"... ok
Enabling workers.dev URL... ok
Deployed.
Worker URL: https://relay-l9k2m1.your-sub.workers.devNon-interactive (CI)
All prompts can be skipped with flags or env vars:
npx cf-relay \
--token "$CLOUDFLARE_API_TOKEN" \
--account <your-account-id> \
--name relay-prod| Flag | Env | Description |
|---|---|---|
| --token <v> | CLOUDFLARE_API_TOKEN | API token. Overrides saved config. |
| --account <id> | — | Account ID. Skips the picker. |
| --name <name> | — | Worker script name. Default: relay-<base36 timestamp>. |
| -h, --help | — | Usage. |
Token resolution order: --token flag → CLOUDFLARE_API_TOKEN env → saved config (~/.config/cf-relay/config.json) → interactive prompt.
Calling the deployed Worker
Send your real request to the Worker URL with two extra headers:
curl https://relay-l9k2m1.your-sub.workers.dev/ \
-H 'x-relay-target: https://api.openai.com' \
-H 'x-relay-path: /v1/chat/completions' \
-H 'Authorization: Bearer sk-...' \
-H 'Content-Type: application/json' \
--data '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"hi"}]}'x-relay-target— origin URL (scheme + host), no trailing slash, e.g.https://api.openai.com.x-relay-path— path + query, e.g./v1/chat/completions?stream=true. Defaults to/.- All other headers (including auth) pass through untouched.
- Streaming responses work — the Worker pipes
response.bodydirectly.
Quick IP check:
curl https://relay-l9k2m1.your-sub.workers.dev/ \
-H 'x-relay-target: https://httpbin.org' \
-H 'x-relay-path: /ip'
# {"origin": "<Cloudflare edge IP>"}Missing header → 400:
curl https://relay-l9k2m1.your-sub.workers.dev/
# {"error":"Missing x-relay-target header"}Use as a proxy in 9router
The deployed URL works as a type: "vercel" proxy pool in 9router — the header contract is identical.
- Dashboard → Proxy Pools → Create pool
- Type:
vercel(reuses the existing relay code path) - Proxy URL: the
cf-relayURL you got above - Attach it to a provider connection. Outbound requests now egress from Cloudflare's edge.
A native type: "cloudflare" may land in 9router later; until then "vercel" is the right value.
Multiple accounts → more IPs
Cloudflare's IP pool is huge but well-known. For more diversity, deploy the same Worker on different Cloudflare accounts:
npx cf-relay --token "$ACCT_A_TOKEN" --name relay-a
npx cf-relay --token "$ACCT_B_TOKEN" --name relay-b
npx cf-relay --token "$ACCT_C_TOKEN" --name relay-cThen round-robin the URLs in your client.
Troubleshooting
This account has no workers.dev subdomain claimed yet — open https://dash.cloudflare.com/?to=/:account/workers-and-pages once, then retry.
TLS handshake failure right after deploy — Cloudflare provisions the wildcard cert for a new subdomain on first use; usually resolves within 30–60s. Retry.
Invalid format for Authorization header — token string is wrong/empty. Re-create the token; tokens are shown only once.
Token has no accessible accounts — token was created with Account Resources → Include → Specific account but pointing at the wrong account, or the token is from a different user.
Uninstall a deployed Worker
Via the dashboard: Workers & Pages → click the script → Manage → Delete.
Via API:
curl -X DELETE \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
"https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/workers/scripts/<NAME>"Security note
The token (and the saved config file at ~/.config/cf-relay/config.json, mode 0600) grants edit access to all Workers on your account. Treat it like a password. Rotate at https://dash.cloudflare.com/profile/api-tokens if you suspect exposure.
License
MIT © dipandhali2021
