This repository contains a Cloudflare Worker / Pages deployment with an admin panel, subscription generation, and configurable transport settings.
Attribution. This is a heavily modified fork of cmliu/edgetunnel, which is itself derived from the upstream edgetunnel/zizifn worker lineage. Licensed under GPL-2.0, same as upstream — see LICENSE. The data plane, build pipeline, admin panel, test suite and documentation here have diverged substantially from upstream; it is not drop-in compatible.
No credentials are stored in this repository.
ADMIN,KEY,UUIDandPATHare supplied at runtime as Cloudflare environment variables. Never commit them. Example hostnames in the docs and tests (example.com) are placeholders, not real deployments.
- Create a Cloudflare Worker or Pages project.
- Set an
ADMINenvironment variable. This is the admin panel password. - Bind a KV namespace using the binding name
KV. - Deploy
_worker_copypaste.js(Dashboard copy-paste) orwrangler_deploy_method_worker/_worker.js(Wrangler). - Open
/adminon your deployed domain and sign in with the admin password.
src/worker.js is the source of truth. npm run build generates two deployable builds from it:
_worker_copypaste.js— for Dashboard "Edit Code" copy-paste. Usesrequest.fetcher.connectfor outbound TCP.wrangler_deploy_method_worker/_worker.js— forwrangler deploy. Uses the documentedconnect()fromcloudflare:sockets.
The two builds are identical except for that outbound-TCP mechanism. Do not paste the Wrangler build into the Dashboard — its cloudflare:sockets import causes Error 1101 there; use _worker_copypaste.js for copy-paste instead.
npm run build
npm run verify-generated
npm test
npm run check
npm run bench:live -- --help
npm run bench:https -- --help
npm run bench:matrix -- --help
npm run bench:suite -- --help
npm run bench:analyze -- --help
npm run bench:compare -- --helpEdit user-facing static defaults in src/core/config.js, then run npm run build.
- Create a new Cloudflare Worker.
- For Dashboard copy-paste, paste the contents of
_worker_copypaste.jsinto the Worker editor. For Wrangler, runwrangler deploy -c wrangler_deploy_method_worker/wrangler.toml. - Add the
ADMINenvironment variable. - Add a KV namespace binding named
KV. - Optional: add a custom domain from the Worker triggers page.
- Create a Cloudflare Pages project.
- Upload the project files or connect this repository.
- Add the
ADMINenvironment variable for production. - Add a KV namespace binding named
KV. - Redeploy after setting the environment variables and bindings.
| Name | Required | Example | Description |
|---|---|---|---|
ADMIN |
Yes | change-me |
Password for the admin panel. |
KEY |
No | quick-link-key |
Optional quick subscription path key. |
UUID |
No | 90cd4a77-141a-43c9-991b-08263cfe9c10 |
Optional fixed UUID. Must be UUID v4. |
HOST |
No | example.com |
Optional explicit host list for generated links. |
PROXYIP |
No | proxy.example.com:443 |
Optional proxy endpoint. |
URL |
No | https://example.com |
Optional fallback home page URL. |
GO2SOCKS5 |
No | *.example.com |
Optional list of host patterns routed through SOCKS5. |
DEBUG |
No | 1 |
Enables debug logging when set to 1 or true. |
ENABLE_KV_LOG |
No | 1 |
Opts in to KV request log writes. Disabled by default to protect KV free-tier write quota. |
OFF_LOG |
No | 1 |
Legacy force-disable for KV request log writes. Takes priority over ENABLE_KV_LOG. |
LOG_TTL_DAYS |
No | 7 |
Number of days to retain append-only KV request log entries. Clamped from 1 to 30 days. |
LOG_READ_LIMIT |
No | 500 |
Maximum number of recent request logs returned by /admin/log.json. Clamped from 1 to 1000. |
ENABLE_KV_PROXY_CACHE |
No | 1 |
Persistent KV proxy-resolution cache. On by default; writes are globally throttled and TTL'd so they cannot exhaust the free-plan KV write quota or drop connections. Set to 0 to disable. |
OFF_PROXY_CACHE |
No | 1 |
Force-disables the persistent KV proxy-resolution cache (the in-memory cache stays on). |
BEST_SUB |
No | 1 |
Enables preferred subscription generator mode when set to 1 or true. |
PRELOAD_RACE_DIAL |
No | 1 |
Enables preload race dialing when set to 1 or true. |
CONNECT_TIMEOUT_MS |
No | 850 |
Optional outbound connect timeout. Values are clamped from 400 to 5000 ms. |
DNS_TIMEOUT_MS |
No | 1200 |
Optional DNS-over-TCP response timeout. Falls back to CONNECT_TIMEOUT_MS when set, otherwise 1200 ms. Values are clamped from 400 to 5000 ms. |
DIAL_STAGGER_MS |
No | 90 |
Optional stagger between clean-IP/proxy candidate dials. Defaults to 90 ms and is clamped from 0 to 500 ms. |
DNS_SERVER |
No | 1.1.1.1:53 |
Optional TCP DNS upstream for tunneled UDP DNS requests. Defaults to 8.8.4.4:53. |
DOH_URL |
No | https://dns.google/dns-query |
Optional DoH endpoint for preload race dialing and proxy-domain resolution. Defaults to Cloudflare DoH. |
FORCE_PROXY_HOSTS |
No | none | Optional comma/newline list of target host patterns that should skip direct dialing and go straight through ProxyIP or the configured chain proxy. Useful for Cloudflare-hosted panel/custom domains that return 5xx when reached from a Worker egress path. Unset by default. |
Open:
https://your-domain.example/admin
Use the ADMIN password to sign in. The panel can update runtime configuration, view logs, and generate subscription links.
- Keep the KV binding name as
KV. - Use a UUID v4 value when setting
UUID. - Redeploy after changing production environment variables.
- The generated subscription host defaults to the current deployed hostname unless
HOSTis explicitly configured. - Proxy endpoint resolution uses a bounded memory cache plus a persistent last-known-good KV cache (on by default). KV writes are globally throttled (at most one every few minutes per isolate) and expire via TTL, so active browsing cannot exhaust the free-plan KV write quota; a write failure is always swallowed and never drops a connection. Set
OFF_PROXY_CACHE=1orENABLE_KV_PROXY_CACHE=0to keep it memory-only. - Request logging is off by default to avoid exhausting Cloudflare's free-tier KV write quota during subscription traffic. Set
ENABLE_KV_LOG=1to store append-only KV entries underlog:entry:; existing legacylog.jsondata is still readable as a fallback when no append-only entries exist. PRELOAD_RACE_DIAL=1can improve first-open latency when DoH is fast and nearby, but it adds a DoH lookup before dialing each new hostname. Leave it off on networks where DoH is slow or blocked.- TCP outbound dialing uses
request.fetcher.connectin the copy-paste build (_worker_copypaste.js) and the documentedconnect()fromcloudflare:socketsin the Wrangler build (wrangler_deploy_method_worker/_worker.js). The Dashboard editor mishandles thecloudflare:socketsimport (Error 1101), so the sockets build must be deployed with Wrangler only. - Tunnel targets are validated before dialing (
validateTunnelTarget): SMTP port 25, localhost, and private/loopback/link-local IPv4 & IPv6 ranges are rejected to reduce SSRF/abuse surface. - gRPC flush timing should be tuned only from measurements. Use
node scripts/grpc-live-smoke-benchmark.mjs --url https://your-domain.example/ --uuid your-vless-uuidto smoke-test a deployed gRPC endpoint before changing batching constants. - Use
node scripts/live-tunnel-benchmark.mjs --url https://your-domain.example/ --uuid your-vless-uuid --transports all --runs 5to compare deployed WS, gRPC, and XHTTP first-byte latency, total time, and success rate before tuning speed-related defaults.
Run benchmarks only against the deployed Worker or custom domain you actually use. For gRPC, a custom domain is usually required. --url, --sni, and --authority should normally be the Worker/custom domain identity, while --front-host is the clean/front domain being tested.
Full baseline suite:
node scripts/run-benchmark-suite.mjs --url https://your-domain.example/ --uuid your-vless-uuid --front-hosts sourceforge.net,www.modrinth.com,www.speedtest.net --sni your-domain.example --authority your-domain.example --bench-target your-benchmark-target.example --out-dir benchmark-runs --prefix baselineThis writes separate reports for latency/burst and HTTPS, and includes download/upload runs when --bench-target is provided.
Latency and front-host stability:
node scripts/live-tunnel-benchmark.mjs --url https://your-domain.example/ --uuid your-vless-uuid --transports grpc --runs 30 --front-host sourceforge.net --sni your-domain.example --authority your-domain.example --service-name / --target neverssl.com --port 80 --profile latencyBurst behavior for Telegram/reel-style request fan-out:
node scripts/live-tunnel-benchmark.mjs --url https://your-domain.example/ --uuid your-vless-uuid --transports grpc --runs 24 --concurrency 6 --front-host sourceforge.net --sni your-domain.example --authority your-domain.example --service-name / --target neverssl.com --port 80 --profile burstReal HTTPS browsing behavior:
node scripts/live-https-benchmark.mjs --url https://your-domain.example/ --uuid your-vless-uuid --runs 10 --front-host sourceforge.net --sni your-domain.example --authority your-domain.example --service-name / --target example.com --port 443 --path /This performs an inner TLS handshake through the gRPC tunnel and reports tlsP50Ms / tlsP95Ms in addition to first-byte and total time.
To compare several front hosts with the same settings and save a baseline report:
node scripts/live-benchmark-matrix.mjs --url https://your-domain.example/ --uuid your-vless-uuid --transports grpc --front-hosts sourceforge.net,www.modrinth.com,www.speedtest.net --profiles latency,burst --runs 30 --sni your-domain.example --authority your-domain.example --service-name / --target neverssl.com --port 80 --out benchmark-baseline.jsonFor real HTTPS front-host comparison, run a separate matrix with an HTTPS target and port 443:
node scripts/live-benchmark-matrix.mjs --url https://your-domain.example/ --uuid your-vless-uuid --transports grpc --front-hosts sourceforge.net,www.modrinth.com,www.speedtest.net --profiles https --runs 10 --sni your-domain.example --authority your-domain.example --service-name / --target example.com --port 443 --out benchmark-https-baseline.jsonAnalyze a saved benchmark report before tuning:
node scripts/analyze-benchmark-report.mjs benchmark-baseline.json --min-runs 10After changing one tuning knob, run the same matrix again with a new output file and compare:
node scripts/compare-benchmark-reports.mjs --baseline benchmark-baseline.json --candidate benchmark-candidate.jsonDownload and upload profiles need a plain HTTP endpoint that serves or accepts the requested payload. The current script sends inner plain HTTP through the tunnel; --port 80 is the inner destination port, not the outer Worker HTTPS port.
For cleaner throughput measurements, deploy the optional benchmark target Worker in benchmarks/ and use its hostname as --target.
node scripts/live-tunnel-benchmark.mjs --url https://your-domain.example/ --uuid your-vless-uuid --transports grpc --runs 10 --front-host sourceforge.net --sni your-domain.example --authority your-domain.example --service-name / --target speedtest.tele2.net --port 80 --http-path /1MB.zip --profile download --timeout 30000
node scripts/live-tunnel-benchmark.mjs --url https://your-domain.example/ --uuid your-vless-uuid --transports grpc --runs 10 --front-host sourceforge.net --sni your-domain.example --authority your-domain.example --service-name / --target httpbin.org --port 80 --http-method POST --body-bytes 1048576 --profile upload --timeout 30000Interpret the result this way:
acceptRateproves the Worker accepted and decoded the tunnel protocol.successRateproves the inner HTTP response looked valid.firstByteP50MsandfirstByteP95Msshow instant-open latency and jitter.totalP50MsandtotalP95Msshow completion time for the chosen payload.throughputP50Mbpsis useful only for download/upload profiles with meaningful payload sizes.- Do not tune
DIAL_STAGGER_MS, DNS preload, queue limits, or gRPC flush settings from a single small-response latency run. - Treat analyzer
FAILsignals as blockers for speed tuning. Fix reliability, target selection, or front-host choice first. - Treat comparator failures as rollback signals unless the failed metric is unrelated to the knob being tested and you intentionally accept that tradeoff.
Use this project only where you have permission and where your use complies with applicable laws, Cloudflare terms, and network policies. You are responsible for your deployment and configuration.