Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,100 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


Update Status Protocol Update Sources License Built With


Collects · Filters · Deduplicates · Categorizes · Publishes Fully automated. No ads. No login. No BS.


🚀 Quick Start · 📁 All Subscriptions · ⚙️ How It Works · 📱 Clients · 🌍 Countries


✨ What Is FastNodes?

FastNodes is a fully automated V2Ray/Xray proxy aggregator. Every 6 hours it wakes up, fetches configs from 170+ public GitHub repositories, tears through them with parallel processing, filters out fake/bogus entries and dead servers, and publishes clean, organized subscriptions — split by protocol, country, and continent.

You get fresh, working proxies in your client without hunting through Telegram channels or sketchy websites. Just copy a link and paste it.


🚀 Quick Start

The fastest way to get going:

What Link
Everything (raw URI list) https://raw.githubusercontent.com/rtwo2/FastNodes/main/sub/everything.txt
Europe https://raw.githubusercontent.com/rtwo2/FastNodes/main/sub/continents/Europe.txt
Iran nodes https://raw.githubusercontent.com/rtwo2/FastNodes/main/sub/countries/IR.txt

Iran users: Start with sub/countries/IR.txt for domestic servers, then try sub/continents/Europe.txt or sub/continents/Asia.txt for exit nodes. Use a client with built-in latency testing (Hiddify, v2rayNG) to find what actually works from your ISP — a node passing FastNodes' check from a GitHub server doesn't guarantee it's reachable from yours.


📁 Subscription Links

All output is plain raw URI lists (.txt) — one config per line, works in every V2Ray/Xray client. There is no Clash/YAML output.

📦 Large lists are split into parts (except everything.txt)

everything.txt is always one single, uncapped file — it's never split, no matter how large it gets.

Every other category (protocols, countries, continents) only stays as xx.txt if it holds 1000 lines or fewer. Once one grows past that, the first 1000 lines always stay in xx.txt — that link never changes — and everything beyond that spills into xx_part2.txt, xx_part3.txt, and so on. (Numbering intentionally starts at part2, not part1, so xx.txt is never ambiguous with a "part one" file.)

sub/protocols/vless.txt          ← first 1000
sub/protocols/vless_part2.txt    ← next 1000
sub/protocols/vless_part3.txt    ← remainder

If a category has ≤1000 nodes, only xx.txt exists — no part files. Subscribe to xx.txt plus as many _partN.txt as your client can handle.

🌐 Everything

The full collection — every alive proxy found this run.

URL
https://raw.githubusercontent.com/rtwo2/FastNodes/main/sub/everything.txt — one single file, never split

🔧 By Protocol

Base path: https://raw.githubusercontent.com/rtwo2/FastNodes/main/sub/protocols/

Recognized protocols: vmess, vless, trojan, ss (shadowsocks), ssr, hysteria, hysteria2 (+ hy2 alias), tuic, naive, anytls, brook, snell, juicity, mieru, wireguard (+ wg alias), socks, socks4, socks5. Each gets its own protocols/<name>.txt if at least one alive node uses it — no code change needed to add a new file, just to recognize a new scheme. An explicit list is used rather than accepting any scheme string — an "accept anything" version was tried and reverted, since it let non-proxy scheme fragments through as if they were real protocols. The most common:

Protocol File
🔵 VLESS vless.txt
🟣 Trojan trojan.txt
🟠 VMess vmess.txt
⚫ Shadowsocks ss.txt
⚡ Hysteria2 hysteria2.txt
🔌 SOCKS / SOCKS5 socks.txt / socks5.txt

Any of these may also have _part2.txt, _part3.txt, ... — check the repo for what currently exists.


🌍 By Continent

Base path: https://raw.githubusercontent.com/rtwo2/FastNodes/main/sub/continents/

Continent File
🌍 Europe Europe.txt
🌏 Asia Asia.txt
🌎 North America NorthAmerica.txt
🌎 South America SouthAmerica.txt
🌍 Africa Africa.txt
🌏 Oceania Oceania.txt

🏳️ By Country

Base path: https://raw.githubusercontent.com/rtwo2/FastNodes/main/sub/countries/ Pattern: sub/countries/XX.txt where XX is the ISO 3166-1 alpha-2 code. A country only gets a file once it has at least 3 alive nodes.

Popular countries:

Country File
🇮🇷 Iran IR.txt
🇩🇪 Germany DE.txt
🇺🇸 United States US.txt
🇳🇱 Netherlands NL.txt
🇷🇺 Russia RU.txt
🇫🇷 France FR.txt
🇬🇧 United Kingdom GB.txt
🇸🇬 Singapore SG.txt
🇯🇵 Japan JP.txt
🇭🇰 Hong Kong HK.txt
🇰🇷 South Korea KR.txt
🇫🇮 Finland FI.txt
🇨🇭 Switzerland CH.txt
🇦🇹 Austria AT.txt
🇨🇦 Canada CA.txt

Around 80 countries are typically available. Just swap the country code in the URL pattern.


📱 Compatible Clients

Every output file is a plain raw URI list — works in virtually every V2Ray/Xray client:

Client Platform How to import
Hiddify Android / iOS / Desktop / Windows Add Profile → URL → paste .txt link
v2rayNG Android Subscription → Add → paste .txt link
Exclave Android Add Subscription → paste link
NekoBox / NB4A+ Android Profile → New → paste .txt link
Shadowrocket iOS Add → Type: Subscribe → paste link
Streisand iOS Add Config → paste link
v2rayN Windows Subscription Group → Add → paste .txt link
Nekoray Windows / Linux Preferences → Subscription → paste link

No Clash/Mihomo YAML output. Earlier versions published a capped, quality-sorted .yaml alongside each .txt. That's gone — see the Changelog for why.


⚙️ How It Works

                    ┌─────────────────────────────────────┐
                    │         170+ GitHub Sources          │
                    │  (fetched in parallel, 30 workers)   │
                    └──────────────────┬──────────────────┘
                                       │
                                       ▼
                    ┌─────────────────────────────────────┐
                    │     Decode & Extract URI Lines       │
                    │  base64 · YAML · HTML · plain text   │
                    └──────────────────┬──────────────────┘
                                       │
                                       ▼
                    ┌─────────────────────────────────────┐
                    │       Parse (per protocol)           │
                    │  dedicated VMess (base64 JSON) and    │
                    │  Shadowsocks (SIP002 + legacy         │
                    │  base64) decoders; URI parse for the  │
                    │  rest                                 │
                    └──────────────────┬──────────────────┘
                                       │
                                       ▼
                    ┌─────────────────────────────────────┐
                    │         Smart Deduplication          │
                    │  key = proto+host+port+credential+    │
                    │    transport+security+sni+path        │
                    │  (keeps different UUIDs/SNIs on the   │
                    │   same IP as distinct nodes)          │
                    └──────────────────┬──────────────────┘
                                       │
                                       ▼
                    ┌─────────────────────────────────────┐
                    │           Host Filter                │
                    │  bare public DNS resolver IPs +      │
                    │  dev-tunnel domains only — see        │
                    │  "What Gets Filtered" below           │
                    └──────────────────┬──────────────────┘
                                       │
                                       ▼
                    ┌─────────────────────────────────────┐
                    │       Alive Check                    │
                    │  TCP: 200 workers · 3.5s · 1 retry    │
                    │  UDP protocols (hysteria/hysteria2/   │
                    │  tuic/juicity/wireguard): pass through│
                    │  untested — see below                 │
                    └──────────────────┬──────────────────┘
                                       │
                                       ▼
                    ┌─────────────────────────────────────┐
                    │  GeoIP Lookup · City → Org → Country  │
                    │  DNS bounded to 3s per host,          │
                    │  resolved once, cached                │
                    └──────────────────┬──────────────────┘
                                       │
                          ┌────────────┼────────────┐
                          ▼            ▼            ▼
                     protocols/   countries/   continents/
                    vless, ss...  IR, DE, US   Europe, Asia
                       (protocols/countries/continents split into
                        ≤1000-line files; everything.txt never splits)

Each run's log is organized into collapsible sections in the Actions log viewer (one per pipeline stage), with a summary table at the end — total sources, raw lines, alive count, elapsed time, and a per-protocol/per-continent breakdown.


🛡️ What Gets Filtered

Every proxy passes through a short, deliberately narrow filter before making it into any output file:

Exact-match, never-a-proxy addresses:

  • Public DNS resolvers: 1.1.1.1, 1.0.0.1, 8.8.8.8, 8.8.4.4 — these always accept TCP but are never actual proxy servers.

Dev-tunnel domains (not dedicated proxy hosts):

  • ngrok.io, ngrok-free.app, loca.lt, serveo.net

Bogus addresses:

  • 0.0.0.0, 127.0.0.1, localhost

Protocol validation:

  • Explicit protocol whitelist — see the full list under "By Protocol" above. An "accept any scheme" version was tried and reverted; it let non-proxy scheme fragments through as if they were real protocols
  • VMess base64 must decode to valid JSON with a real add field
  • Shadowsocks is decoded via a dedicated SIP002/legacy-base64 parser (not generic URI parsing)
  • Everything else must parse as a valid URI with a real hostname/IP
  • Port must be 1–65535

Liveness check:

  • TCP protocols: port must accept a TCP connection within 3.5 seconds from GitHub's runner — retried once if the first attempt fails, so one dropped SYN or a momentarily busy host doesn't wrongly mark a node dead
  • UDP/QUIC-based protocols (Hysteria, Hysteria2, TUIC, Juicity, WireGuard) pass through untested — see below for why

What's deliberately not filtered anymore

Earlier versions blocked every IP inside Cloudflare's published CIDR ranges, plus workers.dev/pages.dev/trycloudflare.com/cloudflare.com/cloudflare.net. That's gone. Routing a proxy through a bare Cloudflare IP or a Workers/Pages endpoint is a common, legitimate way to make it reachable from a censored network — blocking it was throwing away a large share of the nodes most likely to actually work from places like Iran. The FireHOL blacklist was also removed (it wasn't the source of the over-filtering, but it added a slow, unnecessary download every run).

UDP/QUIC-based protocols aren't tested — on purpose, for now

A TCP connect can't validate a UDP/QUIC-based proxy at all, so Hysteria, Hysteria2, TUIC, Juicity, and WireGuard all pass through the alive check untested rather than being run through the wrong transport.

A bounded UDP probe was tried here for one version — it only ever acted on an unambiguous "nothing is listening" signal (an ICMP port-unreachable response) and left every other outcome alone, specifically designed so it could only remove definitively-dead nodes and never wrongly exclude a working one. In practice it still dropped working nodes anyway. The most likely explanation: some networks/firewalls send back a port-unreachable response as an anti-scanning measure even for a live server's first unexpected packet, which broke the core assumption the check depended on. It's been removed.

A full QUIC/TLS handshake would be the properly accurate test, but isn't implemented either: it needs the right ALPN string, which isn't consistent across Hysteria2/TUIC implementations, and depends on libmsquic being available on the runner, which isn't guaranteed. Getting either wrong would make the handshake fail for a genuinely alive server, silently turning "untested" into "wrongly marked dead" for the whole protocol — worse than the current pass-through, and not something verifiable without a real environment to test it in. This is left as a known gap rather than a half-working guess.


🧠 Smart Deduplication

Most collectors dedup on host:port only, which silently drops configs with different UUIDs on the same server. FastNodes deduplicates on the full config identity:

protocol + host + port + credential(UUID/password) + transport + security + SNI + path + REALITY pbk/sid

This means if a server hosts multiple VLESS configurations (common with Xray setups), all of them are kept — exactly matching what you'd get subscribing to the source directly. SNI and path are part of the key too: domain-fronted setups commonly reuse the same host+port+UUID across several different front domains (that's the whole point of fronting), and those are kept as distinct nodes rather than collapsed into one, since different SNIs can have very different real-world reachability from a censored network. Same logic for REALITY's public key and short-id — different values there mean a different trust anchor, not a duplicate.


🏷️ How Nodes Are Named

Each remark is geo info + server address — no port, no protocol:

🇩🇪 Frankfurt, DE · Hetzner | example.com
🇺🇸 United States · DigitalOcean | 1.2.3.4
🌐 DigitalOcean | 5.6.7.8
🌐 Unknown | 7.8.9.10
🇩🇪 Frankfurt, DE | [2001:db8::1]

Resolution order:

  1. City + country, if GeoLite2-City has an entry for the IP — with the hosting org appended when the ASN database identifies one (e.g. Frankfurt, DE · Hetzner). Knowing the provider is useful even when the city is known, since two servers in the same city can behave very differently depending on who's hosting them.
  2. Country + hosting org, if city data is missing but the ASN database identifies the provider. This is the common case — most proxy IPs are VPS addresses that simply have no city-level entry in MaxMind's free database, so the org name is often the only extra detail available.
  3. Org only (🌐 <org>), if neither city nor country resolved at all but the ASN database still recognizes the IP.
  4. 🌐 Unknown, if nothing resolves.

IPv6 hosts are shown bracketed in the remark ([2001:db8::1]) so they don't read ambiguously next to the | separator — this only affects the displayed name, not the actual connection link.

If two alive nodes land on the exact same geo + host (e.g. same server, different port or protocol), they get #1, #2, ... suffixes. The node set is sorted by host, then port, then protocol before numbering, so which node gets which suffix stays stable across runs instead of shuffling every time the collector runs.


🔄 Update Schedule

UTC Tehran (IRST)
00:00 03:30
06:00 09:30
12:00 15:30
18:00 21:30

You can also trigger a manual run from the Actions tab.


📂 Repository Structure

FastNodes/
├── .github/
│   └── workflows/
│       └── collect.yml              # Runs every 6h, drives everything
├── ProxyCollector/
│   ├── Collector/
│   │   └── ProxyCollector.cs        # Core engine — fetch, parse, filter, output
│   ├── Configuration/
│   │   └── CollectorConfig.cs       # Reads source URLs from env vars
│   ├── Services/
│   │   └── IPToCountryResolver.cs   # GeoIP lookup — city → org → country, DNS-bounded
│   ├── Models/
│   │   ├── CityInfo.cs
│   │   └── CountryInfo.cs
│   ├── GeoLite2-City.mmdb           # Auto-downloaded each run (P3TERX mirror)
│   ├── GeoLite2-Country.mmdb        # Auto-downloaded each run
│   └── GeoLite2-ASN.mmdb            # Auto-downloaded each run (org/hosting-provider names)
└── sub/
    ├── everything.txt               # All alive proxies — one file, never split
    ├── protocols/                   # Per-protocol .txt, chunked at 1000 lines
    │   ├── vless.txt (+ _part2, ...)
    │   ├── trojan.txt
    │   ├── vmess.txt
    │   ├── ss.txt
    │   └── hysteria2.txt
    ├── countries/                   # Per-country .txt, chunked the same way
    │   ├── IR.txt                   # Iran
    │   ├── DE.txt                   # Germany
    │   └── ... (~80 countries)
    └── continents/                  # Per-continent .txt, chunked the same way
        ├── Europe.txt
        ├── Asia.txt
        ├── NorthAmerica.txt
        ├── SouthAmerica.txt
        ├── Africa.txt
        └── Oceania.txt

⚠️ Honest Notes

About testing from GitHub servers: The TCP alive check runs from GitHub Actions (Azure, US/EU regions). A node that passes TCP from there might still be blocked or throttled inside Iran, and some Iran-friendly nodes may look dead to the checker. This is a fundamental limitation of any cloud-based collector, and there's no ranking or scoring applied to work around it — just try more than one node.

This project does not:

  • Host or operate any proxy servers
  • Guarantee any node works in your specific location or ISP
  • Collect any user data whatsoever

All configs are sourced from publicly available GitHub repositories. Credit goes to all the original collectors and server operators.


🙏 Sources

FastNodes aggregates from 170+ public repositories. Major contributors include: AvenCores, MatinGhanbari, barry-far, Epodonios, Surfboardv2ray, 10ium, NiREvil, F0rc3Run, mahdibland, 4n0nymou3, Mosifree, sakha1370, wuqb2i4f, V2RayRoot, sevcator, igareck, youfoundamin, HosseinKoofi, Argh94, Mahdi0024, liketolivefree, MahsaNetConfigTopic, hamedcode, AzadNetCH, Leon406, roosterkid, ebrasha, Danialsamadi, LalatinaHub, Farid-Karimi, and many others.


📝 Changelog

v6.7

Reverted v6.6. The "accept any scheme" approach was undone — back to an explicit whitelist, now expanded to cover more real proxy protocols: ssr, naive, anytls, brook, snell, juicity, mieru, wireguard (+ wg alias) alongside the original set. wireguard/wg also join the UDP pass-through set alongside hysteria/hysteria2/tuic/juicity, since it's UDP-based too.

Removed the UDP probe added in v6.4 — it was designed to only ever confirm unambiguous death (ICMP port-unreachable), but in practice it was still dropping working Hysteria2/TUIC nodes. Most likely cause: some networks/firewalls send back a port-unreachable response as an anti-scanning measure even for a live server's first unexpected packet, which broke the assumption the check depended on. These protocols pass through untested again until a properly reliable check exists.

Found and fixed the likely cause of the node-count regressions reported after v6 (fewer nodes in country categories, nodes with longer/CDN-style hostnames disappearing, trojan counts dropping sharply): the TCP probe's per-attempt timeout had been cut from 3.5s to 2s a few versions back, to offset the cost of the retry added around the same time. For hostname-based nodes, ConnectAsync(host, port, ...) does DNS resolution and the TCP handshake inside that same window — 2s often wasn't enough for both. This would hit CDN/Workers-fronted nodes (typically longer, less-common hostnames) and Trojan specifically (which is almost always paired with a real domain for TLS SNI to mean anything, unlike protocols commonly run on bare IPs) harder than the rest of the traffic. Restored to 3.5s. A fresh run is needed to confirm this was the full explanation, but it's a concrete, verifiable mechanism, not a guess.

Redesigned the workflow log. Added a startup banner, wrapped each pipeline stage in a collapsible GitHub Actions group (fetch, parse/dedup, alive check, GeoIP/rename, save) so the log reads as distinct sections instead of one long scroll, and added a formatted summary table at the end (sources, raw lines, alive count, elapsed time, per-protocol and per-continent breakdown).

v6.6

Removed the protocol whitelist entirely, per explicit request. Previously, only vless/vmess/trojan/ss/hysteria/hysteria2/tuic/socks/socks5 were recognized — both at the extraction stage (a regex that only matched those exact scheme names) and again at the parse stage (a ValidProtocols gate). Now:

  • The extraction regex matches any scheme:// pattern (2-16 chars, valid URI scheme charset), not a fixed alternation list.
  • The parse-stage gate was replaced with a small blocklist (NeverAProxyScheme) covering only schemes that can never be a proxy config — http, https, ftp, mailto, and similar plain web links — so those don't create junk "protocol" output files. Everything else is accepted.
  • A source using something less common (naive://, anytls://, brook://, etc.) now gets its own protocols/<scheme>.txt automatically, with zero code changes needed.

Worth knowing: this trades a little precision for completeness. Since almost anything with a valid-looking scheme://host:port shape now gets through to parsing, a stray URI fragment that isn't really a proxy config (unlikely, but possible with sufficiently permissive sources) could end up creating a small, mostly-junk protocol file rather than being silently dropped at the door. The downstream liveness check still filters out anything that isn't actually reachable, which catches most of that.

v6.5

A from-zero review of the whole project (not building on any prior review list) turned up three more real bugs:

  • Hysteria v1 was being mislabeled as Hysteria2. NormalizeProto merged anything starting with "hysteria" into hysteria2 — but Hysteria v1 and Hysteria2 are different, incompatible protocols. Real v1 links were silently getting relabeled and dumped into protocols/hysteria2.txt, which would break for any client that distinguishes the two. Only the hy2 alias now maps to hysteria2; plain hysteria stays its own category.
  • wss:// was matching the ss:// (Shadowsocks) pattern. The protocol-extraction regex had no boundary before the scheme name, so any WebSocket-Secure URL embedded in a source (extremely common in JSON config snippets) had its trailing ss:// fragment misdetected as a Shadowsocks link and fed into the parser as garbage. Added a negative lookbehind so ss:// can no longer match as a substring of wss://.
  • REALITY's public key and short-id weren't part of the dedup identity. Two REALITY configs on the same host+port+UUID but a different pbk/sid are cryptographically distinct trust anchors, not duplicates — but neither field was captured at all, so they silently collapsed into one entry. Both are now parsed and included in the dedup key.

Also added a real (deliberately bounded) UDP liveness probe for Hysteria2/TUIC — see the dedicated section above for the honest limits of what it actually catches; it's a small, safe improvement, not a substitute for a real handshake.

v6.4

Added real (but deliberately bounded) UDP liveness checking for Hysteria2/TUIC, instead of leaving them completely untested as in v6.3. A full QUIC/TLS handshake was considered and rejected — it needs an ALPN string that isn't consistent across implementations and depends on libmsquic being available on the runner, and getting either wrong would silently turn "untested" into "wrongly marked dead" for every node of that protocol, which is a worse outcome than doing nothing and not something verifiable without a real environment to test it in.

Instead, these nodes now get a single UDP probe that only ever acts on an unambiguous "nothing is listening" signal (ICMP port-unreachable) — any response, any timeout, or any other outcome leaves the node in, same as before. This can only remove nodes that are definitively dead; it can't introduce new false negatives the way a fragile full handshake could have.

v6.3

Fixed a real bug found from a run log: the log showed Loaded GeoLite2-*.mmdb printed 4 times each, which meant the GeoIP resolver was being constructed multiple times instead of once. Cause: the lazy-singleton pattern (_resolver ??= new IPToCountryResolver()) isn't atomic, and with GeoIP resolution running 100-way parallel, several threads could all see it as unset at the same moment and each build their own instance — wasting the mmdb file handles/memory of the extra copies and quietly fragmenting the caching across them. Switched to Lazy<T>, which guarantees single construction under concurrent access.

Repo cleanup (the remaining items from the last review):

  • Removed the sing-box install/cache steps from the workflow — nothing in the collector ever referenced sing-box or the SingboxPath env var, so it was pure dead weight on every run.
  • Stopped committing GeoLite2-Country.mmdb — it's re-downloaded from scratch at the start of every run regardless of what's in the repo, so the committed copy was always overwritten before being read; it only added a multi-MB binary diff to history whenever MaxMind refreshed the data.
  • City/Country/Org GeoIP lookups now share one DNS resolution cache instead of each independently re-resolving the same hostname — a hostname-based node used to trigger up to 3 separate DNS lookups for the exact same host.
  • Added the 17 small territories (Antarctica, Åland, Bonaire, Faroe Islands, etc.) that had a flag emoji but no continent mapping, so a node there wasn't silently excluded from every continents/ file.

Parsing & dedup accuracy:

  • The dedup key now includes SNI and path. Domain-fronted setups commonly reuse the same host+port+credential across several different SNI values (fronting through a shared IP with different front domains) — those were being silently collapsed into a single entry even though different SNIs can have very different real-world reachability from a censored network.
  • Hysteria2 and TUIC are UDP/QUIC-based, not TCP — but they were going through the same TCP-connect liveness check as everything else. A working Hysteria2/TUIC server has nothing listening on TCP for that port, so this was very likely producing a lot of false "dead" verdicts (and the ones that did pass were passing by coincidence, not because the check meant anything for them). These protocols now skip the TCP check entirely and pass through untested — a full UDP/QUIC liveness check would need an actual protocol handshake, which is a separate, bigger feature.
  • Considered adding a third retry attempt to the TCP alive check. Decided against it: the first attempt plus one retry already resolves nearly all transient failures, and a third attempt would add real time to every run (most of the ~half of candidates that fail two attempts are genuinely unreachable, not flaky) for very little additional yield.

v6.2

  • Hosting org name now shown alongside city, not just as a fallback when city is missing (e.g. Frankfurt, DE · Hetzner instead of just Frankfurt, DE).
  • ASN/org lookup is now also tried when both city and country resolution fail, instead of going straight to 🌐 Unknown.
  • Fixed a rare edge case where a hosting org name that fully matched the "legal suffix" strip pattern (e.g. an org string that was just "LLC") could leave a dangling · with nothing after it.
  • Long org names now truncate at a word boundary with instead of a hard character cut.
  • Duplicate #N suffixes are now assigned deterministically (sorted by host, then port, then protocol) instead of following ConcurrentBag's arbitrary enumeration order — previously, which physical server got called "#2" vs "#3" could change every run even when the underlying node set hadn't changed.
  • Bare IPv6 hosts are now bracketed in the displayed remark ([2001:db8::1]) so they don't read ambiguously next to the | separator. Connection links are unaffected.

v6.1

  • everything.txt is now always a single, uncapped file — it no longer gets split into parts (only per-protocol/country/continent files do).
  • Chunk size raised from 500 to 1000 lines per file.
  • TCP alive check now retries once on failure instead of giving up after a single attempt, so a one-off dropped connection doesn't wrongly mark a working node as dead.
  • Per-attempt TCP timeout lowered from 3.5s to 2s, to offset the added time from the retry.

v6.0

Removed:

  • Clash/Mihomo .yaml output entirely — every category is now raw .txt only.
  • top1000 capped files — testing from GitHub's own servers isn't a meaningful "best nodes" signal for users elsewhere, so the concept was dropped rather than kept as dead weight.
  • The two-pass TCP "stability" check and all latency/quality-based sorting — replaced with one simple pass: does the port open, yes or no.
  • The Cloudflare CIDR filter and the FireHOL blacklist. The CIDR filter in particular was blocking a large share of nodes that front through Cloudflare on purpose — a common, legitimate way to stay reachable from a censored network.

Added:

  • Line-count-based file chunking for every category except everything.txt: xx.txt always holds the first N lines (a stable link that never disappears), overflow goes to xx_part2.txt, xx_part3.txt, ... (numbering starts at 2 so xx.txt is never confused with a "part one" file).
  • A dedicated Shadowsocks parser (SIP002 mixed form + legacy full-base64 form) — the previous generic URI parser only handled a rare cleartext form and was silently dropping most real-world ss:// links.
  • GeoLite2-ASN lookup: when a proxy's IP has no city-level data (the common case for VPS/hosting-provider IPs), the hosting org name (e.g. Hetzner, DigitalOcean) is shown instead of falling back to bare country.
  • ~25 new source repositories.

Changed:

  • Node naming format is now geo info + server address, no port, no protocol (e.g. 🇩🇪 Frankfurt, DE | example.com) — previously included protocol instead of address, and before that included host:port directly in the name.
  • GeoIP DNS resolution now runs fully async with a bounded 3s timeout per lookup and negative-caches failures, instead of blocking synchronously with no timeout (this alone cut GeoIP-stage runtime from ~11 minutes to well under a minute in testing).
  • Workflow now explicitly requests contents: write permission — some repos default GITHUB_TOKEN to read-only, which was silently failing every push after a successful local commit.

Earlier

No changelog was kept before v6.0.


If FastNodes helps you, a ⭐ means a lot

Auto-updated every 6 hours · Built with C# .NET 9 · Powered by GitHub Actions

About

⚡ Auto-updated free V2Ray proxy collector — 50,000+ alive nodes every 6h · sorted by quality · by country, continent & protocol

Topics

Resources

Stars

66 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages