# Cf Proxy

> Deploy a free VLESS proxy/VPN node on Cloudflare Workers/Pages using edgetunnel. Automates the full setup — code download, UUID generation, Pages deployment, free domain registration, DNS configuration, custom domain binding, and client configuration. Trigger when the user asks to set up a Cloudflare proxy, deploy a VLESS node, build a free VPN on Cloudflare Workers/Pages, configure edgetunnel, or manage/update/fix an existing CF proxy node. 触发场景：搭建翻墙节点、Cloudflare 代理、VLESS 代理、edgetunnel 部署、科学上网节点搭建， 或管理/更新/修复/重建已有的 Cloudflare 代理节点。

- Skill: `lewisliu007/cf-proxy` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add lewisliu007/cf-proxy`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lewisliu007/cf-proxy/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: LewisLiu007 (https://skillmd.com/u/lewisliu007)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lewisliu007/cf-proxy

---


# cf-proxy

Deploy a free VLESS proxy node on Cloudflare Pages + edgetunnel, bypassing GFW censorship via WebSocket over TLS through Cloudflare's CDN.

## Architecture

```
Client (Shadowrocket / v2rayN / Clash)
  ↓ VLESS over WebSocket over TLS (port 443)
Custom Domain (CNAME → *.pages.dev)
  ↓
Cloudflare CDN (global edge network)
  ↓
Cloudflare Pages Function (edgetunnel _worker.js)
  ↓ TCP outbound
Target Website
```

**Key design decisions:**
- `workers.dev` is blocked by GFW at the TLS SNI layer — a custom domain is mandatory
- Cloudflare Workers custom domains require DNS hosted on CF — unsuitable for free domains
- **Cloudflare Pages supports CNAME-based custom domains** — the best free-tier approach
- edgetunnel (cmliu/edgetunnel, 30k+ stars) supports VLESS/Trojan/Shadowsocks
- **Never create a CNAME at the zone apex** — it destroys SOA/NS records; always use a subdomain

## Pre-flight Check

Before starting, verify dependencies are available:

```bash
# Check Node.js
node --version || echo "MISSING: Node.js required (install via brew install node)"

# Check wrangler
npx wrangler --version 2>/dev/null || echo "MISSING: wrangler (will install via npx)"

# Check gh CLI (for downloading edgetunnel)
gh --version || echo "MISSING: gh CLI (install via brew install gh)"
```

If any dependency is missing, use `AskUserQuestion` to confirm whether to install it or if the user prefers to handle it themselves.

## Setup Workflow

### Phase 1: Collect User Inputs

Use `AskUserQuestion` to gather required information:

1. **Cloudflare API Token** — must have Workers/Pages permissions. If the user doesn't have one, guide them to: Cloudflare Dashboard → My Profile → API Tokens → Create Token
2. **Cloudflare Account ID** — found on the Workers & Pages overview page
3. **Project name** — default: `edgetunnel-pages`
4. **Custom domain** — ask if the user has a domain. If not, Phase 4 will register a free one
5. **UUID** — offer to auto-generate (recommended) or let the user specify
6. **Admin password** — offer to auto-generate (recommended) or let the user specify

### Phase 2: Download edgetunnel

```bash
mkdir -p ~/edgetunnel && cd ~/edgetunnel

# Download _worker.js via GitHub API (git clone often times out in China)
gh api repos/cmliu/edgetunnel/contents/_worker.js --jq '.content' | base64 -d > _worker.js

# Verify download
[ -s _worker.js ] && echo "OK: $(wc -c < _worker.js) bytes" || echo "FAILED: empty file"
```

### Phase 3: Generate Config & Deploy

1. **Generate credentials:**
```bash
# UUID for VLESS authentication
UUID=$(uuidgen | tr '[:upper:]' '[:lower:]')
echo "UUID: $UUID"

# Admin password for management panel
ADMIN=$(openssl rand -base64 12)
echo "ADMIN: $ADMIN"
```

2. **Create wrangler.toml:**
```toml
name = "edgetunnel-pages"
main = "_worker.js"
compatibility_date = "2025-11-04"
keep_vars = true

[vars]
UUID = "<generated-uuid>"
ADMIN = "<generated-password>"
```

3. **Deploy to Cloudflare Pages:**
```bash
npx wrangler pages deploy . --project-name edgetunnel-pages
```

The output will contain a `*.pages.dev` URL — save it for DNS configuration.

### Phase 4: Obtain a Free Domain (if needed)

If the user has no domain, register a free one from **DNSExit** (dnsexit.com):
- Provides free second-level domains (e.g., `*.linkpc.net`), valid for 2 years
- Supports custom DNS records via API

After registration, add a CNAME record via DNSExit API:
```bash
curl -s "https://api.dnsexit.com/dns/" \
  -H "apikey: <DNSEXIT_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "yourname.linkpc.net",
    "update": [{"type":"CNAME","name":"vless","content":"your-project.pages.dev","ttl":300}]
  }'
```

**Critical: use a subdomain** (e.g., `vless.yourname.linkpc.net`), never the root domain. A root CNAME will destroy the zone's SOA/NS records and make the entire domain unreachable.

### Phase 5: Bind Custom Domain to Pages

Via Cloudflare API:
```bash
curl -s -X POST \
  "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/pages/projects/<PROJECT_NAME>/domains" \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"name":"vless.yourname.linkpc.net"}'
```

Or via Dashboard: Workers & Pages → select project → Custom domains → Add.

Wait for DNS propagation (typically 1-5 minutes, can take up to 30 minutes):
```bash
# Verify DNS resolution
dig vless.yourname.linkpc.net CNAME +short
```

### Phase 6: Verify & Configure Client

1. **Verify the node is reachable:**
```bash
curl -v --max-time 10 "https://vless.yourname.linkpc.net"
# Should return a valid HTTP response (not timeout)
```

2. **Access management panel** to get connection details:
```
https://vless.yourname.linkpc.net/<ADMIN_PASSWORD>
```

3. **Client configuration** (Shadowrocket / v2rayN / Clash):
   - Protocol: VLESS
   - Address: `vless.yourname.linkpc.net`
   - Port: 443
   - UUID: the generated UUID
   - Transport: WebSocket
   - TLS: enabled
   - Host/SNI: `vless.yourname.linkpc.net`
   - Path: `/?ed=2048`

### Phase 7: Optional — Bind KV Storage

KV provides persistent configuration storage:

```bash
npx wrangler kv namespace create KV
# Note the returned namespace ID, then add to wrangler.toml:
# [[kv_namespaces]]
# binding = "KV"
# id = "<namespace_id>"
```

## Cloudflare Free Tier Limits

| Resource | Free Quota | Impact on Proxy |
|----------|-----------|----------------|
| Requests | 100,000/day | WebSocket connection = 1 request; messages are free. Plenty for daily browsing |
| CPU time | 10 ms/request | Only computation, not I/O wait. Proxy is I/O-bound, typically <3ms |
| Bandwidth | **Unlimited** | No egress fees — the biggest advantage |
| Memory | 128 MB/isolate | Sufficient |
| Outbound connections | 6/request | Single VLESS connection needs only 1 outbound |
| KV reads | 100,000/day | Sufficient |
| KV writes | 1,000/day | Sufficient |
| Pages builds | 500/month | No impact unless redeploying frequently |

## Known Limitations

1. **No UDP support** — Workers handle HTTP/WebSocket only; cannot proxy UDP traffic (games, VoIP)
2. **Speed depends on CDN routing** — typically 5-50 Mbps depending on client-to-edge path quality
3. **100K daily request cap** — heavy use may hit this limit; proxy stops with error 1027
4. **GFW may block custom domain SNI** — domain rotation may be needed
5. **Cloudflare ToS risk** — low-profile personal use is generally fine; large-scale/commercial use risks account termination
6. **Not suitable for low-latency use cases** — CDN relay adds latency vs direct connections

## Troubleshooting

| Symptom | Cause | Fix |
|---------|-------|-----|
| Shadowrocket connection timeout | workers.dev blocked by GFW | Must use custom domain |
| curl returns NXDOMAIN | DNS not propagated or zone corrupted | Check DNS records, wait for propagation |
| Error 1027 | Daily request limit exceeded | Wait for UTC midnight reset, or upgrade to paid plan |
| Error 1102 | CPU time or memory exceeded | Check worker code; rarely triggered for proxy workloads |
| WebSocket disconnects immediately | UUID mismatch | Verify client UUID matches worker config |
| Admin panel 404 | ADMIN var not set | Set ADMIN in wrangler.toml [vars] |
| Root domain NXDOMAIN after CNAME | Zone apex CNAME destroyed SOA/NS | Delete root CNAME, use subdomain instead, rebuild zone |

## Upgrading to Paid Plan

Workers Paid plan ($5/month):
- Requests: 10 million/month (no daily cap)
- CPU time: 30s/request (default), up to 5 min
- All other limits significantly increased

## References

- edgetunnel: https://github.com/cmliu/edgetunnel
- Cloudflare Workers limits: https://developers.cloudflare.com/workers/platform/limits/
- Cloudflare Pages limits: https://developers.cloudflare.com/pages/platform/limits/
- Cloudflare Workers pricing: https://developers.cloudflare.com/workers/platform/pricing/

