Protecting a Minecraft server from attacks

Connection floods on the Java port, bot joins no network filter can see, and how to secure query and RCON on a public server.

Game servers8 min read

On this page
  1. What gets attacked
  2. The Java port, with connection floods
  3. The server list ping
  4. The query listener, if you turned it on
  5. Bedrock, which is UDP throughout
  6. Applying protection on a Clouduxe server
  7. What a network filter cannot see: bot joins
  8. online-mode is the single most important setting
  9. Whitelisting
  10. prevent-proxy-connections
  11. Anti-bot plugins
  12. A proxy in front
  13. Per-player packet rate
  14. Securing RCON
  15. Backups are the part that actually recovers you
  16. Related

Attacks on a Minecraft server arrive at two levels, and they need two different answers. Packet floods aimed at your port are handled by the network filter in front of the server. Bot joins are valid Minecraft connections that no network filter can tell apart from players, and they are stopped inside the game.

Packet floodsHalf-open handshakes,abandoned connections,status-request floodsBot joinsComplete thehandshake, speak theprotocol, log inThe network filterUpstream, then Prism on the machine,with your port rule's ceilingsInside the gameonline-mode, a whitelist, an anti-bot plugin, aproxy in frontDropped hereWell formed:passesStopped here
Two kinds of attack, stopped in two places. Packet floods are dropped by the filter in front of the server. Bot joins are well formed, so they pass it, and only settings inside the game can turn them away. Illustration.

What gets attacked

The Java port, with connection floods

Java gameplay is TCP on one port, 25565 by default. The attacks that arrive there are floods of connection attempts: half-open handshakes that never complete, or completed connections that are opened and abandoned in volume. What they consume is connection state, CPU in the network stack, and the server's own connection handling, long before anyone reaches the login screen.

This is a packet-rate problem, and it is what protection at the network level exists to deal with.

The server list ping

Every client showing your server in its multiplayer list sends a status request. The server answers with the MOTD, the version, the player counts, and usually a server icon. It is a normal part of how Minecraft works.

As an attack it is a flood of status requests, each costing a TCP connection and a reply. Because it is TCP and arrives on the same port as gameplay, a limit on that port covers both, and there is nothing separate to configure. It is not a reflection or amplification vector, because a TCP handshake cannot be completed with a spoofed source address.

The query listener, if you turned it on

enable-query in server.properties opens a separate UDP listener, on query.port, which defaults to the same number as server-port. This one is different in kind. It is UDP, so the source address in a request can be forged, it is unauthenticated, and its full response is considerably larger than the request that triggered it. That combination is the shape attackers use to reflect traffic at someone else while your server does the sending.

Leave enable-query=false unless a monitoring tool genuinely needs it. If you do need it, give it its own port rule with a rate ceiling, the same as any other open port.

Bedrock, which is UDP throughout

Bedrock gameplay itself runs over UDP on 19132, including its server discovery ping. There is no handshake to hide behind, so the UDP rule on a Bedrock server carries more weight than the TCP rule does on a Java one.

Applying protection on a Clouduxe server

Traffic reaches your server through two filters. The first sits upstream on the network and removes traffic before it arrives, with nothing for you to configure. The second is Clouduxe Prism, running on the machine itself, and that is the one you set up. Everything you tune there hangs off an inbound port rule: which ports are reachable, the packet-rate ceilings on each, whether traffic is checked against the protocol the service speaks, and which source addresses are allowed or denied.

  1. Apply the preset for your edition

    Minecraft (Java) writes a TCP 25565 rule. Minecraft (Bedrock) writes a UDP 19132 rule. Both arrive with packet-rate ceilings already set, and preset rules generally arrive with protocol validation switched on, which a rule you add by hand does not. See Game presets for what each one contains and how to reverse it.

  2. Correct the port if you changed it

    A preset writes the default port. If your server-port is anything else, edit the rule to match, or the preset has protected a port nothing is listening on and left yours without a rule. See Port rules.

  3. Fit the limits to your own traffic

    A preset is sized for a typical server of that type, and yours is not typical. Player count, plugins, a proxy in front, and whether you run query all move the numbers. Work out what your server does on a normal busy evening, then set the ceilings above that with headroom. See Rate limits for what each ceiling bounds and how to choose a number.

  4. Add a rule for anything else you expose

    A second Minecraft server, a Bedrock listener alongside Java, a Geyser port, a web map. Each is its own port and its own rule.

What a network filter cannot see: bot joins

A bot join is a real Minecraft connection. It completes the TCP handshake, speaks the protocol correctly, and logs in. Every packet is well formed. From the network's point of view it is indistinguishable from a player arriving, because in protocol terms it is one.

The damage is done above the network. Bot joins fill player slots, fire join and quit events through every plugin you have installed, force chunk loading around spawn, write to your database, and flood your logs. A large enough wave takes the server down through the work each join creates, without ever exceeding a packet rate ceiling.

Everything that stops it lives in your server configuration.

online-mode is the single most important setting

online-mode=true in server.properties makes the server verify every joining account against Mojang's session servers. A bot then needs a real, paid account per connection, which changes the economics completely.

online-mode=false means anyone can connect claiming any username, without limit and without cost. It is what makes a server a target, and it has a second problem: anyone can join using your administrator's username and inherit their permissions. Leave it true unless a proxy in front of the server is doing the authentication instead.

Whitelisting

For a private or community server, this is the complete answer:

/whitelist add SomePlayer
/whitelist on

Set white-list=true and enforce-whitelist=true in server.properties so the setting survives a restart and so players already connected are removed when they are taken off the list. Nobody who is not on the list gets past login. It is not usable for a public server, which is the whole reason the rest of this section exists.

prevent-proxy-connections

Another server.properties option. It asks the session server whether the address a player is joining from matches where their account authenticated, which blocks some VPN and proxy connections. It also blocks legitimate players who use a VPN, so test it against your own community before leaving it on.

Anti-bot plugins

An anti-bot plugin sits in the login flow and holds a connection until it behaves like a real client. The techniques vary, and a good one combines several: limiting joins per address over time, requiring a reconnect before the first successful join, checking that the client moves and looks around the way a human does, and holding new connections in a holding area before letting them into the world.

Choose one that is maintained for your Minecraft version, and test it: the failure mode of an aggressive anti-bot is blocking real players, which looks identical to an outage.

A proxy in front

A proxy such as Velocity, or the older BungeeCord, runs as a separate process in front of one or more backend servers. It absorbs the login flow, so a bot wave hits the proxy rather than your world, and it is where network-wide anti-bot, queue, and limbo plugins run.

Three things make that safe, and you need all three:

  • Bind the backend privately. Set server-ip on the backend to an address only the proxy can reach.
  • Restrict the backend's port to the proxy. Its rule should accept the proxy's address and nothing else. See Port rules.
  • Turn on forwarding with a secret. Velocity's modern forwarding uses a shared secret set in velocity.toml and in Paper's config/paper-global.yml, so a backend rejects a connection that does not carry it. BungeeCord's older forwarding, enabled with bungeecord: true in spigot.yml, has no such secret, which is precisely why modern forwarding was introduced.
PlayersConnect to the proxyDirectly to the backendCould pick anyusername, anoperator's includedVelocity proxyDoes theauthentication, thenforwards the playerwith the secretBackend serveronline-mode=falseserver-ip: an address only the proxy can reachPort rule: accepts the proxy's address andnothing elseModern forwarding: rejects a connection withoutthe secretRefused by1, 2 and 3
A proxy in front of a backend running online-mode=false. Players arrive through the proxy, which authenticates them; a direct connection to the backend is refused. The numbers match the three safeguards above, and you need all three. Illustration.

Per-player packet rate

server.properties carries a rate-limit option, the number of packets a single connected player may send before being disconnected, with 0 disabling the check. It bounds a client that floods packets after joining rather than one that floods connections. Set it too low and a real player on a poor connection gets kicked during a burst, so raise it from disabled cautiously and watch for complaints.

Securing RCON

RCON is a remote console: connect to rcon.port, send the password from rcon.password, and issue server commands. It is enabled by enable-rcon and defaults to TCP 25575.

Treat it as the most dangerous thing on the machine. The protocol is plaintext, so the password and every command cross the network unencrypted. Vanilla applies no rate limiting or lockout to failed authentication attempts. Command access to a Minecraft console is usually equivalent to full control of the server, and with the right plugin installed it reaches the filesystem.

  1. Leave enable-rcon=false unless something specific needs it.

  2. Never open the RCON port. Do not add an inbound rule for it. Reach it through an SSH tunnel instead:

    Shell
    ssh -L 25575:127.0.0.1:25575 you@203.0.113.10

    Then point your RCON client at 127.0.0.1 port 25575. Nothing is exposed.

  3. Use a long random password, unique to this server:

    Shell
    openssl rand -base64 24
  4. Set broadcast-rcon-to-ops=true so RCON commands appear in-game to operators. If someone else is using it, you find out.

The same reasoning applies to a web management panel, a dynamic map, or a plugin's remote API. If it does not need to be on the public internet, tunnel it rather than opening a port for it.

Backups are the part that actually recovers you

Filtering stops traffic. It does not undo a griefing incident, a duplication exploit, or a plugin that corrupted a world on update. The thing that recovers those is a recent copy stored somewhere other than the server. See Backups.

If you have worked through the checklist and something still does not add up, open a ticket with the port, the protocol, and the public address of an affected player.

Need a server, or a hand with one?

Pick a plan to deploy a server, or open a ticket in the panel. Support runs 24/7/365.