What a join flood actually does
Somebody rents a botnet or a stresser, points it at your server address, and it starts connecting. Each connection performs a real handshake and a real login attempt. On a server running in online mode, answering means generating an encryption request, decrypting the client's reply and asking Mojang's session servers whether the account is genuine.
That last step is the interesting one, because it is a network round trip to a third party. The server cannot answer it locally and cannot answer it quickly. Under a flood, those round trips queue up, and the thread pool that services them stops servicing anything else. Your tick rate collapses. Nobody is technically kicked, but the world becomes unplayable and people leave.
If the server runs in offline mode instead ("cracked"), the cryptography and the round trip disappear — and the attack gets easier, because now every connection is admitted straight into the world with whatever username it claimed. A hundred bots standing in spawn is not a subtle failure.
The signatures, and why the obvious one stops working
The classic tell is the usernames. Cheap attack tools generate them, so you see a wall of eight-character strings with no vowel pattern a person would choose. It is easy to spot in a log and easy to filter on, which is exactly why serious attacks stopped doing it. Accounts can be bought, and a flood driven by real accounts with real names passes every name-shaped filter you can write.
The other signatures are structural rather than cosmetic:
- Rate. A great many logins per second, from a great many addresses, at a moment nothing else explains.
- Address distribution. Overwhelmingly datacenter, VPN and proxy ranges rather than residential ones — because botnets are rented infrastructure. This is a hint, not a verdict: plenty of real players connect from a VPN.
- Reuse across victims. Attacks on game servers are overwhelmingly rented botnets, and the same addresses move from one server to the next. An address that already proved hostile somewhere else should not get a clean first impression at yours.
- Behaviour after joining. This is the strongest one, and it is covered below.
The layers, and what each one bounds
There is no single check that catches all of this, and any product that claims one is describing the check attackers will beat next. What works is a ladder, where each rung costs an attacker more than the last and costs a real player nothing.
1. Concurrency and rate limits per address. The cheapest possible screen. It does not stop a distributed attack — that is what the next layers are for — but it stops one machine from costing you an unbounded amount of work, and it costs nothing to apply. Login budgets and ping budgets are kept separate, because a status ping is cheap and normal to send often while a login is expensive and normal to send rarely; one shared budget would have to be loose enough for pings, which makes it useless for logins.
2. Reputation. An address that has already proven hostile against another server carries that history. The critical design rule here is that a reputation score must never block on its own — the worst it should do is raise the tier of proof asked for. Otherwise one customer's bad day becomes a blocklist for everybody, and a mistake becomes permanent. It also means only high-confidence signals should be reported into such a system: a protocol violation is the address's own doing, while a rate-limit rejection says as much about the server's configured limits as about the address.
3. Proving an account. Asking a connection to hold a real, currently-authenticated Minecraft account is a wall a bot farm cannot fake and cannot buy cheaply at scale. It is also the most expensive check to run, so it has to be budgeted per address — a flood must never turn your protection into a load generator against Mojang — and it has to fail open. A session-server outage that becomes your outage is a worse failure than briefly admitting an unverified player.
4. Proving a human. Accounts can be bought in bulk, so account ownership alone is not the end of the ladder. The tier above it asks a person to read a short code off their screen and type it back — seconds for a player, an OCR pipeline or a human per account per join for an attacker. See Minecraft CAPTCHA and player verification.
5. Behaving like a client. Every gate above is a bounded cost: pay it once and you are in. The one thing no bot pays is the continuous cost of behaving like a Minecraft client for the entire time it is connected.
The layer bots cannot pay
A real Minecraft client runs a tick loop twenty times a second, and everything it does is shaped by that loop. It answers keep-alives on its next tick rather than the instant it reads them, so round-trip times scatter across a tick's width instead of being flat. It sends a wide vocabulary of packet types. Its view moves, because a hand on a mouse is never perfectly still. A scripted client that answers on read, speaks four packet types and never rotates the camera is doing none of that — and faking it means running a real tick loop, which is the expensive half of being a client.
The essential design rule is that none of these signals blocks anything on its own, because each has an honest explanation. An AFK player never moves. A modded client speaks a vocabulary of its own. A LAN player has a ping flat enough to look synthetic. One signal is a coincidence; several at once is a script.
Chat spam is a group phenomenon
The other thing bots do once they are in is advertise. Ten accounts posting one template with a random token glued on the end is invisible if you look at any single session — each message is unique, so exact-match filters never fire — and unmistakable the moment you look at the group. Collapsing messages to the shape they share, then noticing when several distinct sessions produce the same shape inside a short window, is what catches it. The distinction that matters: five players typing "gg" within ten seconds is a normal end to a round, while five players typing the same sentence with a different random string appended each time is one program driving all five.
What to do while it is happening
- Do not switch to offline mode to "reduce load". It removes the one check that costs an attacker something real.
- Do not start banning usernames. You will be renaming a variable the attacker controls, and you will eventually ban a real player.
- Get the screening off your machine. Every mitigation applied on the server itself is applied after the server has already paid. If you are not behind a filtering proxy, this is the change that matters.
- Turn on a standing challenge if you have one. Protection that waits for traffic to look like an attack is right most of the time; when you already know trouble is coming, removing that condition and challenging everybody up front is cheap insurance.
- Keep the logs. Which addresses, which usernames, which rejection reasons. It is the only thing that turns "we got attacked" into something you can act on.
The general incident-response sequence for web properties applies here too — see How to stop a DDoS attack — and the architectural picture is in Minecraft server DDoS protection.