ModsJava
AntiVPN-X
AntiVPN-X is plugin blocks any sort of VPN mobile hotspots proxy botting and MORE!
⬇ Download on SpigotPlease leave a preview of any kind
Detects and blocks VPN, proxy, Tor and datacenter connections when players join.
One jar, every platform. Each host reads its own descriptor and loads only its own entry point — the same pattern ViaVersion uses. A fully commented config.yml is generated on first start and is identical everywhere.
Supported platforms
- Proxy — Velocity 3.3 / 3.4 / 3.5 · BungeeCord (and Waterfall forks)
- Server — Paper, Purpur, Folia, Spigot — 1.19 and newer
- Sponge — SpongeAPI 9 → 17 (SpongeVanilla and SpongeForge)
- Bedrock — Nukkit and PowerNukkitX (a native Bedrock server, no Geyser needed)
- Hybrids — Mohist, Arclight, Banner, Magma, Cardboard
- Embedded — Minestom, called from your own code
- Java 17+
Works with zero configuration — the offline lists and four of the thirteen providers need no registration.
Honest scope: what "any VPN" means here
There is no technique that catches every VPN with certainty. The hard case is a self-hosted WireGuard endpoint on a residential IP — from the server's point of view that traffic is indistinguishable from an ordinary player. Anyone claiming 100 % detection is selling something.
What this plugin does instead is stack six independent layers so the realistic cases are covered and the remaining gap is small:
- Permanent address memory — everything already seen once. Free, no lookup at all.
- Offline blocklists (X4BNet VPN + datacenter, Tor exit nodes) — commercial VPN exit nodes, VPS/cloud ranges, Tor. Free, offline, microseconds.
- Statically blocked ranges from config.yml — whatever you add by hand. Free.
- Online IP reputation (13 providers) — VPN / proxy / Tor / relay / residential-proxy flags, fraud scores. One HTTP call, then remembered.
- ASN + ISP keyword heuristics — self-hosted VPNs on hosting providers no list knows yet. Free, runs on provider data.
- Country filter — region rules, independent of VPN detection. Free.
The trade-off is false positives. Blocking hosting also blocks players behind corporate networks, some university connections and anyone genuinely proxying through a VPS. iCloud Private Relay is caught by the relay flag. Mobile carriers are not blocked by default. Start with actions.kick: false (monitor-only) for a day and read detections.log before enforcing.
Permanent address memory — what keeps the free API tiers usable
The first time an address connects it goes through the full pipeline. The verdict is then written to known-addresses.json and reused: clean addresses are waved through with no API call, flagged ones are blocked with no API call either.
This changes what the provider quotas are spent on. Instead of scaling with logins, cost scales with distinct addresses ever seen — on a settled server that is a handful of new addresses a day, so even a 100/day free tier stops being the binding constraint.
Code (Text):
known-addresses:
enabled: true
revalidate-clean-after-days: 0 # 0 = remember forever
revalidate-blocked-after-days: 0
enabled: true
revalidate-clean-after-days: 0 # 0 = remember forever
revalidate-blocked-after-days: 0
Setting both to 30 still removes roughly 97 % of the API calls while letting a wrong verdict correct itself within a month. That is the setting to use unless you have a reason not to. Either way, /antivpn known forget <ip> fixes a single address on the spot.
Detection modes
- ANY — block if any source flags the address, stopping at the first hit. Highest detection rate, usually one HTTP call. Default.
- FIRST_AVAILABLE — trust only the highest-priority source that answers. Lowest API usage.
- MAJORITY — query everything; block when more than half the answering sources agree and at least min-flagged-sources did. Fewest false positives.
- ALL — block only on unanimous agreement.
Providers
Thirteen are supported, plus a configurable one. Four need no registration and are on by default:
- ip-api — no key, 45 req/min
- ipapi-is — no key, separate vpn / proxy / tor / datacenter booleans, names the VPN service
- proxycheck — 100/day anonymous, 1000/day with a free key
- iplocate — full privacy block incl. iCloud relay
Your own endpoint. providers.custom takes a URL and the names of the JSON fields that mean vpn, proxy, tor, hosting, a risk score and so on — dotted paths, so security.vpn reaches into a nested object. For a network that already runs its own reputation API, or a service that appeared after this build.
Each provider has its own token-bucket rate limiter, because exceeding a free tier gets the server's address banned for hours — which would silently disable detection.
Extra protections
- Anti-bot / DDoS guard (on) — measures the server-wide join rate. When a flood starts, unknown addresses are refused straight from memory: no blocklist match, no HTTP call, no provider quota spent. Under attack the plugin does less work per connection, not more. Your regulars keep playing throughout.
- Reconnect challenge (on, during an attack) — refuses the first sighting of a name-and-address pair and lets the second through. Attack tooling moves on; a person clicks once more. Under attack this replaces the blanket refusal, so a brand new player can still get in by joining twice.
- Alt limit (off) — refuse an address once it exceeds N distinct accounts. A VPN no source knows about is still obvious when a dozen accounts arrive from one address.
- Returning-player trust (off) — skip the check for accounts that joined cleanly before, so a regular who travels or tethers to a phone does not get locked out.
- Bedrock exemption (on) — never check Geyser/Floodgate players. Identified by Floodgate's UUID shape, so no Floodgate dependency is needed.
- Connection throttle (off) — refuse an address that reconnects faster than a person would. A throttled connection never reaches a provider, so it protects the API quota too.
- Per-name throttle (off) — the same, but per username, so a bot that rotates IPs between attempts is still caught.
- Username filter (off) — refuse a name by length, allowed characters or a regex. Generated names are often the only thing a bot flood has in common. Costs no lookup at all.
- Runtime blacklist — block an address or CIDR range with /antivpn blacklist, optionally with an expiry (30m, 12h, 7d). The only way to block a range without editing config.yml and reloading.
- Warn-only flags (off) — report a flag without enforcing it. Watch the noisiest flag on a live server and see exactly who it would have caught before switching it on.
- Discord webhook (off) — post detections to a channel as an embed, on a background thread so a slow webhook never delays a connection.
- Update checker (on) — one plain-text request to SpigotMC a minute after start and every six hours after. Nothing about your server is sent.
A false positive on someone who is live streaming is the one failure mode with a privacy cost: they get refused and their IP address appears on camera in front of an audience. So by default the kick screen shows a short reference code instead:
Code (Text):
CONNECTION REFUSED
A VPN was detected on your connection.
Please disable it and join again.
REFERENCE 8A3F1C
A VPN was detected on your connection.
Please disable it and join again.
REFERENCE 8A3F1C
The reference code is a one-way hash of the address, so it is stable, quotable in a support ticket, and reveals nothing. Resolve it with /antivpn ref 8A3F1C.
26 languages
Player- and staff-facing text ships in 26 bundled files — English plus Spanish, Russian, German, French, Portuguese, Polish, Turkish, Chinese, Indonesian, Italian, Vietnamese, Dutch, Korean, Czech, Thai, Hungarian, Arabic, Japanese, Ukrainian, Swedish, Danish, Romanian, Slovak, Hebrew and Lithuanian.
Files are copied to plugins/AntiVPN-X/lang/ on first start and are never overwritten — edit any string there and run /antivpn reload. A missing key in a translation falls back to English, so an incomplete file never shows a raw key on a player's kick screen. With follow-player-language: true, staff alerts follow each viewer's own Minecraft client language.
Commands
/antivpn (alias /avpn), permission antivpn.admin:
- /antivpn reload — reload config.yml
- /antivpn check <ip|player> — force a fresh check and print every field
- /antivpn analyze <ip|player> — everything already known, without spending a lookup
- /antivpn stats — counters, cache hit rate, per-provider queries and errors
- /antivpn cache info|clear — inspect or drop cached verdicts
- /antivpn known info|lookup|forget|clear — the permanent address memory
- /antivpn ref <code> — resolve a kick reference code back to an address
- /antivpn alts <ip|player> — accounts seen on an address
- /antivpn trust info|revoke|clear — returning-player trust
- /antivpn lists info|reload — blocklist status; reload forces a re-download
- /antivpn whitelist add|remove|list <player|ip> — runtime exemptions (accepts CIDR)
- /antivpn blacklist add|remove|list|clear <ip|cidr> [duration] [reason] — runtime blocks
- antivpn.bypass — never checked
- antivpn.notify — receives in-game detection alerts
- antivpn.admin — access to /antivpn
- antivpn.altlimit.bypass — exempt from the accounts-per-address limit
- antivpn.throttle.bypass — exempt from the connection rate limit
- antivpn.namethrottle.bypass — exempt from the per-name connection rate limit
Code (Text):
general:
enforcement-stage: LOGIN # LOGIN = the bypass permission works; PRE_LOGIN = blocks earlier
fail-open: true # let players in when every source is unreachable
countries:
mode: DISABLED # DISABLED | WHITELIST | BLACKLIST
list: ["EU", "US"] # ISO country codes, continent codes, or both
detection:
mode: ANY # ANY | FIRST_AVAILABLE | MAJORITY | ALL
block:
vpn: true
hosting: true # the single most effective flag
mobile: false # do not ban phones
enforcement-stage: LOGIN # LOGIN = the bypass permission works; PRE_LOGIN = blocks earlier
fail-open: true # let players in when every source is unreachable
countries:
mode: DISABLED # DISABLED | WHITELIST | BLACKLIST
list: ["EU", "US"] # ISO country codes, continent codes, or both
detection:
mode: ANY # ANY | FIRST_AVAILABLE | MAJORITY | ALL
block:
vpn: true
hosting: true # the single most effective flag
mobile: false # do not ban phones
Installation
Drop the jar into plugins/ and restart — the same file works on every supported platform.
Install it on the proxy or on the servers, not both. The proxy refuses the connection earlier and one config covers the whole network. Use the server install only when there is no proxy, or the backend is reachable from the internet directly.
On first start plugins/AntiVPN-X/ is created with config.yml (heavily commented), known-addresses.json, lists/, cache.json, whitelist.json, blacklist.json and detections.log.
config.yml is never rewritten while it is valid. When an update adds options, the missing keys fall back to their defaults, a warning lists them, and the current defaults are written to config.new.yml so you can diff them by hand.
Self-repair on a broken config. A YAML typo does not leave detection silently off. The broken file is moved to config.old.yml, a fresh default is written in its place, the plugin comes up on those defaults and logs exactly what went wrong. This applies to an in-game /antivpn reload as well as to startup, so a bad edit never leaves the server unprotected — and nothing is lost.
Tuning for fewer false positives
- Run with actions.kick: false for a day, then read detections.log.
- If legitimate players are hit, first try detection.block.hosting: false — it is the noisiest flag by a wide margin.
- Still noisy? Switch detection.mode to MAJORITY and enable a second provider.
- For a single bad range, add it to local-lists.static-allowed-ranges or use /antivpn whitelist add 203.0.113.0/24.
- If an ISP name collides with a keyword (Google Fiber vs "google"), add it to asn-heuristics.keyword-exceptions.
Not supported on their own — those are mod loaders with no Bukkit or Bungee API. There are two practical answers, and most networks already use one: run a hybrid (Mohist, Arclight, Banner), which gives you the Bukkit API on top and is supported above; or put Velocity or BungeeCord in front of the modded server and run AntiVPN-X on the proxy, which refuses the connection before it ever reaches the modded server.
Privacy
Only the connecting player's IP address is sent to the enabled providers — never names or UUIDs. The free ip-api.com endpoint is plain HTTP (their restriction); supplying a pro key switches the plugin to HTTPS automatically. Disable that provider if it matters to you — the offline lists work without it.
The plugin submits an anonymous server profile to bStats: server software and version, player count, Java version, OS and plugin version. No addresses, names, UUIDs or detection results ever leave the server. Opting out is bStats' own switch ( plugins/bStats/config.yml), so turning it off once covers every plugin on the server.
Deliberately not included
MySQL / shared storage (a large untestable surface — ask if you run a multi-proxy network and want it), a PlaceholderAPI expansion (it would make the jar Bukkit-aware for a handful of scoreboard values), and MCLeaks alt-account lookups (a second external API answering a different question than this plugin asks).
Requirements
- One of the platforms above
- Minecraft 1.19 → 26.1
- Java 17 or newer
- Outbound HTTPS (for the blocklists and the reputation APIs)
AntiVPN-X is a free Minecraft Java mod. Compatible with Minecraft 1.19, 1.20, 1.20.6, 1.21 and newer. Downloaded 79 times (via Spigot). Download it and open it directly in the game.