Open-source project
cmliu/CF-Workers-CheckProxyIP avatar
cmliu/CF-Workers-CheckProxyIP

CF-Workers-CheckProxyIP: a one file Worker that probes ProxyIP candidates over TLS

部署在 Cloudflare Workers 上的轻量级 ProxyIP 验证工具。它能够快速、准确地检测代理IP的可用性,帮助用户筛选出有效的代理服务器。

732 stars479 forksJavaScriptGPL-3.0

At a glance

What is it?
CF-Workers-CheckProxyIP is a single file Cloudflare Worker that resolves a domain or IP into candidate endpoints, opens a TCP and TLS connection to each one, and reports the exit address, address family and Cloudflare colo. It is aimed at people filtering a list of reverse proxy addresses, not at general proxy testing.
Who is it for?
Adopt this if you are running a batch of reverse proxy addresses behind Cloudflare Workers and need a quick screen for which ones still answer and which address family they exit with. Skip it if you need a general purpose proxy checker, if you cannot accept that every check calls out to third party probe hosts, or if you need per target authentication.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One Worker, five repository files, and a job narrower than the name suggests

The repository is unusually small: .gitignore, LICENSE, README.md, _worker.js and demo.png. The README calls the project a lightweight ProxyIP verification tool deployed on Cloudflare Workers and states the current scope plainly. It renders a detection page, parses single or batched input of IP, IPv6 or domain, validates candidate ProxyIP entries through a built in /check route, shows exit address, geolocation, ASN and a map route, and keeps a history of recent single inputs in the browser. The same file serves the page, the parsing routes and the probe logic, so there is no build step, no package manifest and no server to run. That is the appeal for a self-hosted check: you paste one JavaScript file into a Worker and the page exists. It also means there is nothing to configure beyond one optional environment variable, BEIAN, which replaces the footer HTML.

Resolution turns a domain into candidates through TXT, A and AAAA lookups

Before anything can be probed, the input has to become a host and port pair. The /resolve route parses the host and port, defaulting to 443, then applies a special rule: a domain containing a .tp and port segment forces that port, so abc.tp8443.example.com is treated as port 8443 regardless of what the input said. If the input is already an IPv4 or IPv6 address it is returned directly, with bare IPv6 normalised to the bracketed form. Otherwise the Worker queries TXT, A and AAAA records concurrently through Cloudflare DoH at cloudflare-dns.com/dns-query. TXT answers are split on commas into separate candidates, while A and AAAA answers inherit the input port. The response is a flat array of candidate strings, and a domain that resolves to nothing comes back as an error object with the message Could not resolve domain.

The probe is a TLS connection plus two echo hosts, and the answer is in the exit address

The check route is where the actual work happens. The Worker connects to the candidate with cloudflare:sockets, performs a TLS handshake, and then fetches two fixed probe URLs, ipv4.090227.xyz and ipv6.090227.xyz. Those hosts echo back the address the request came from, which is how the tool learns the exit IP and whether the candidate exits on IPv4, IPv6 or both. The result payload carries the candidate, a success flag, the target IP, the remote port, a response time in milliseconds, the Cloudflare colo that ran the check, and a probe_results object with the ip, ipType, colo, asn, asOrganization, country, city and loc fields for each family. The route accepts timeoutMs, defaulting to 9999, and readLimit, defaulting to 65536 bytes. It can be called with a single proxyip or a list of proxyips in a JSON body, which is how the browser fans out 32 concurrent checks.

Deploying the Worker and running a first check

There is nothing to install. The README describes deployment through the Cloudflare Workers console, and the whole application is the single _worker.js entry point, so the deploy step is uploading that file as a Worker script and pointing a domain or workers.dev address at it. Open the deployed page and you get the input area, the summary cards, the result list and the map detail panel. For a single target, type an address and press Enter; for a batch, switch on the batch toggle, put one target per line, and press Ctrl + Enter or Cmd + Enter. The page also accepts a path, so a shareable link such as the one below fills the input box and starts a check on load.

text
https://your-worker.example.workers.dev/8.223.63.150:443

If you prefer to call the route yourself, the single target form of the request is a JSON body with one candidate.

json
{
  "proxyip": "203.0.113.10:443"
}

The batch form takes a list of candidates and returns one result object per entry, with the resolved targets and an error field for the ones that did not resolve.

json
{
  "proxyips": [
    "203.0.113.10:443",
    "[2001:db8::10]:443"
  ]
}

The /resolve endpoint answers with the candidate list directly, for example a single IPv4 input producing one entry.

json
[
  "8.223.63.150:443"
]

Batch mode is shaped by Cloudflare subrequest accounting, not by taste

Two limits shape how the page behaves under load. The /resolve-batch endpoint accepts at most 15 targets per request, and the README states the reason: staying under the subrequest ceiling of the free Workers plan. The browser groups domains into batches of 15, retries a batch if it does not respond within 3 seconds, and gives up on it after 3 consecutive failures. Only domains go through the resolver, since IPv4 and IPv6 are recognised and normalised in the browser and never submitted. The final candidate list is deduplicated twice, once by original input order and again by IP and port before any probe runs, and all surviving candidates are checked with 32 concurrent requests. Single mode keeps a different kind of state: the last ten inputs are stored in browser local storage under the key cf_proxy_history, so the history never leaves the client.

Every check calls out to third parties, which is the real cost of the design

The README lists the external dependencies and they are worth reading before a deployment. The two probe hosts decide the result, Cloudflare DoH resolves the domains, and speed.cloudflare.com/locations supplies the coordinates behind the map panel. The front end pulls Leaflet 1.9.4 from unpkg, map tiles from tile.openstreetmap.org, country flags from ipdata.co and fonts from Google Fonts. So a single check touches a hostname you do not control for the exit address, a public DNS resolver, a location feed and several CDNs for the interface. The exit IP you discover is therefore visible to the operator of the probe host, and the page cannot be deployed in an isolated network without rewriting those references. The README also notes that if you replace /check with your own detection service, you must keep the documented field names or change the front end checkIP() function and the result rendering together.

It probes TLS sockets, so it cannot tell you whether an address works as a proxy

The scope is narrow and the distinction matters. The Worker opens a TCP connection, completes a TLS handshake and fetches a probe URL through that connection, then reports the exit address. What it does not do is send a request through the candidate as an HTTP or SOCKS forward proxy to a third party site and check the response body. A candidate can therefore pass this check and still be useless for the thing you care about, which is relaying traffic for a specific origin. The tool also carries its own opinions: the TLS handshake must succeed against port 443 by default, IPv6 needs the bracketed form, and the .tp and port rule silently overrides whatever you typed. If your screening need is a single address, the online demo at the project homepage already does this in a browser. If you need repeatable checks on a schedule, on your own machine, or against a specific origin, this Worker is the wrong shape of tool.

Licence ambiguity, no releases, and a last push on 2026-09-15

The project is published under GPL-3.0, which matters if you plan to modify _worker.js and serve the result to users over a network, though note that ordinary GPL copyleft obligations are triggered by distribution, not by serving a modified version over a network. There is a wrinkle worth resolving yourself: the README's project structure block annotates the LICENSE file as an MIT licence, while the licence identifier on the repository is GPL-3.0. Read the LICENSE file in the repository and settle the question before you redistribute anything, and take no legal advice from this paragraph. On maintenance, the last push was on 2026-09-15 and the repository has no GitHub releases, so there is no changelog to track and no versioned artifact to pin. The absence of releases also means there is no upgrade path: you take the current _worker.js and you replace it wholesale when it changes.

Editorial conclusion

Adopt this if you are running a batch of reverse proxy addresses behind Cloudflare Workers and need a quick screen for which ones still answer and which address family they exit with. Skip it if you need a general purpose proxy checker, if you cannot accept that every check calls out to third party probe hosts, or if you need per target authentication. Before you rely on it, read the LICENSE file in the repository, because the project is published under GPL-3.0 while the README file tree annotates the licence as MIT, and check that your own Cloudflare plan tolerates the 15 domain per request and 32 concurrent check limits the code imposes.

Frequently asked questions

What does CF-Workers-CheckProxyIP actually test on a candidate address?

It connects to the candidate over TCP and TLS using cloudflare:sockets, then fetches ipv4.090227.xyz and ipv6.090227.xyz to read back the exit address. The result reports success, response time, the Cloudflare colo, and whether the candidate exits on IPv4, IPv6 or both. It does not exercise the candidate as an HTTP or SOCKS forward proxy.

Which input formats does CF-Workers-CheckProxyIP accept?

IPv4, IPv4 with a port, bare IPv6 (normalised to the bracketed form on port 443), bracketed IPv6 with a port, a domain, and a domain with a port. Domains are resolved over Cloudflare DoH using TXT, A and AAAA records, and a domain containing a .tp and port segment forces that port number.

Does the tool store my check history on the server?

No. In single target mode the last ten inputs are kept in the browser under the local storage key cf_proxy_history. The Worker itself reads only one environment variable, BEIAN, which overrides the footer HTML when it is set.

Official sources

  1. cmliu/CF-Workers-CheckProxyIP on GitHub
  2. Issues
  3. License: GPL-3.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cmliu-cf-workers-checkproxyip.svg)](https://hysenlabs.com/projects/cmliu-cf-workers-checkproxyip)

Was this useful?