Team Fortress 2: protecting a TF2 server from DDoS attacks
Which ports a Team Fortress 2 server really needs, how to limit A2S queries, split packets, RCON and rates without dropping out of the server browser, and at what attack volume only filtering in front of the server still helps.
A Team Fortress 2 community server that loses every player at once in the middle of a round and then disappears from the server browser for minutes rarely has a hardware problem. In most cases an attack is running against 27015/UDP. This article shows how to protect a TF2 server from DDoS attacks: first what you can do yourself in the next ten minutes at no extra cost, then the point where those measures end for physical reasons, and finally what has to happen in the network in front of the server.
Everything here refers to a Source dedicated server installed with SteamCMD (srcds_run -game tf) on Debian 12, Debian 13, Ubuntu 22.04 LTS or Ubuntu 24.04 LTS. The commands are written for root. As a normal user, put sudo in front of them. If the attack is running right now: do not change anything yet and do not restart the server, capture the measurements from section 9 first. Once the attack is over they are gone.
Why Team Fortress 2 servers need DDoS protection
Team Fortress 2 has been free to play since 2011, and that alone shifts the economics of an attack. An attacker has an unlimited supply of throwaway accounts, pays for none of them and risks nothing when one gets banned. What costs money in a paid game costs one minute here.
On top of that comes a trait that sets TF2 apart from most other games. Since the "Meet Your Match" update in July 2016 there is no Quickplay that distributes new players across community servers. New players end up on Valve servers in Casual mode, and community servers can only be found through the server browser. A server that falls out of that list effectively no longer exists for new players, even if the server process is running perfectly. An attack that merely pushes you out of the list has therefore already achieved its goal.
The targets follow from this: permanently running community servers with regulars (24/7 2Fort, trade, jailbreak, surf, dodgeball, Mann vs. Machine), league servers with a fixed match time in ETF2L, RGL and ozfortress play, and servers whose operator has just banned somebody. The trigger is almost never technical. For what a DDoS attack actually is, read What is a DDoS attack?.
The ports that actually matter on a TF2 server
A TF2 server needs exactly one port open to the internet: 27015/UDP. Everything else can be switched off, belongs behind a restriction, or only runs outbound. This table is the basis for every firewall rule further down:
| Port | Protocol | Purpose | Reachable from outside? |
|---|---|---|---|
| 27015 | UDP | Game traffic and the A2S server query on the same port, set with -port |
yes, mandatory |
| 27015 | TCP | RCON, remote administration via rcon_password |
no, your own address only |
| 27020 | UDP | SourceTV (STV), set with tv_port, disabled with -nohltv |
only if you actually broadcast |
| 27005 | UDP | Client port used outbound by the player (+clientport) |
no, needs no rule on the server |
| 26900 upwards | UDP | Steam port of the server process (-steamport), counts up per additional instance |
no, outbound to Steam only |
| 80 and 443 | TCP | FastDL for maps and content (sv_downloadurl), if hosted on the same machine |
only if the download lives there |
With several instances on one machine the numbers count up: 27016, 27017 and so on for the game, 27021 and 27022 for SourceTV. The configuration file lives at tf/cfg/server.cfg and is read again on every map change.
Why the shared port 27015 is the most sensitive spot
On TF2, game traffic and the server query share the same UDP port. There is no separate query port. An A2S_INFO request is exactly 25 bytes long: four bytes FF FF FF FF, one byte 0x54 and the 20 byte string "Source Engine Query" with a terminating null. The reply carrying server name, map, player count and tags is a multiple of that. The US agency CISA puts the amplification factor of the Steam protocol at 5.5 in alert TA14-017A.
Because UDP has no connection setup and source addresses can be spoofed, this was an open amplification hole for years: an attacker queried other people's Source servers using the victim's address as the sender, and those servers sent their replies to the victim. A2S_PLAYER and A2S_RULES always required a challenge that had to be fetched first, A2S_INFO did not. Only in December 2020 did Valve add a challenge for A2S_INFO as well: instead of the data packet, the server may reply with an S2C_CHALLENGE that the querying party has to repeat, proving that it did not spoof the source address.
That defuses the reflection, but it does not end the trouble. Every query packet still arrives at your server and costs CPU time before it is answered or dropped. And an attacker flooding your server directly needs no amplification in the first place.
What you can do yourself before spending money
This section is the longest one, deliberately so. A properly configured TF2 server survives small and medium attacks on its own, no matter where it is hosted.
1. Take inventory: what is listening, and with which start line
Before you write a single rule, find out what your server offers to the outside world. Do not guess, look:
ss -lntup
Anything bound to 127.0.0.1 or ::1 needs no firewall rule. Anything on 0.0.0.0 or [::] is reachable from the internet, including the MySQL database a stats plugin brought along and the web server holding your FastDL files. Compare the result against your start line:
./srcds_run -game tf -console \
-port 27015 -steamport 26901 -nohltv \
+maxplayers 24 +map ctf_2fort +sv_pure 1 \
+sv_setsteamaccount YOUR_GSLT_TOKEN
Every port in that line is a deliberate decision. How the underlying server is installed is covered in Install a game server with SteamCMD.
2. Only leave open the ports TF2 really needs
A public TF2 server needs exactly one rule to the outside, plus RCON for your own address. With UFW, in this order, so you do not lock yourself out:
ufw allow 22/tcp comment 'SSH'
ufw allow 27015/udp comment 'TF2 game and A2S'
ufw allow from 203.0.113.10 to any port 27015 proto tcp comment 'RCON'
ufw default deny incoming
ufw default allow outgoing
ufw --force enable
ufw status verbose
Replace 203.0.113.10 with your own address. SourceTV is deliberately missing here: if you do not broadcast, start the server with -nohltv and 27020/UDP is never occupied in the first place. That halves the UDP surface a TF2 server exposes. If you do broadcast league matches, add ufw allow 27020/udp, and then a tv_password belongs in the config.
The full walkthrough including the way back is in Setting up the UFW firewall without locking yourself out. If it does happen anyway: on KVM root servers and dedicated servers from KernelHost you reach the machine through the VNC console in the customer area, which works independently of the guest system's network stack.
3. Limit A2S queries without dropping out of the server browser
This is where the most expensive mistake in this field happens: blocking or bluntly rate limiting 27015/UDP throws out your own players and finishes the attack on the attacker's behalf. Because game traffic and queries share the port, the limit has to run between the packet types, not across the port.
The engine ships three console variables for that, and they belong in tf/cfg/server.cfg:
sv_max_queries_sec 3
sv_max_queries_sec_global 60
sv_max_queries_window 30
The first limits the answered queries per source address, the second caps the total across all addresses, the third sets the averaging window in seconds. They protect the CPU from producing answers for nothing. Defaults differ between games and builds, so find sv_max_queries in the server console tells you which values your server actually knows.
The second value is the delicate one on TF2, because it caps replies across all addresses. Set it too low and during a query flood your server stops answering the listing services as well, which makes it vanish from the server browser, the only path on which new players find you. Start generously and tighten only once you can measure that legitimate queries get through.
One layer down, the same traffic can be separated cleanly. Every connectionless packet of the Source engine begins with four set bytes (0xffffffff), while traffic from already connected players does not carry that header. A rate limit can sit on exactly that, without touching the game traffic:
table inet tf2 {
chain input {
type filter hook input priority -10; policy accept;
udp dport 27015 @th,64,32 0xffffffff \
meter a2sflood { ip saddr limit rate over 10/second burst 20 packets } drop
}
}
Load the file with nft -f. Priority -10 makes sure the rule is evaluated before the UFW filter chain, and @th,64,32 reads the first four bytes after the UDP header.
4. Catch split packet floods that show up as NET_GetLong
This attack is a quirk of the Source engine and hits TF2 particularly hard, because TF2 still runs on the older engine branch. Besides ordinary connectionless packets, the engine knows split packets: they begin with FE FF FF FF instead of FF FF FF FF and announce that a larger message follows in several parts. The server has to buffer the parts and wait for the rest.
That is exactly what gets abused. An attacker sends masses of announced but never completed fragments with spoofed source addresses. CPU load climbs, the game stutters, and the server log fills with lines containing NET_GetLong. A single machine is enough for this, and it takes almost no bandwidth. Operators regularly report it as a DDoS attack although the line is nearly empty.
Since a regular TF2 client has little reason to send split packets to the server, a tight limit is defensible here:
udp dport 27015 @th,64,32 0xfffffffe \
meter tf2split { ip saddr limit rate over 5/second burst 10 packets } drop
The line belongs in the same chain as the rule from section 3. One of the few legitimate reasons for client uploads is removed on top of that with sv_allowupload 0 (see section 7).
5. Take RCON off the open internet
The Source engine RCON protocol transmits the password in clear text over TCP. Anyone able to read the path between you and the server has your RCON password afterwards, and whoever has RCON can change the map, ban every player and stop the server. That is not a DDoS problem but a takeover, and it still gets reported as an attack on a regular basis.
Never leave rcon_password empty and never let it be guessable, a value from openssl rand -base64 32 will do. Add a brake against login attempts:
rcon_password "YOUR_RANDOM_VALUE"
sv_rcon_maxfailures 3
sv_rcon_minfailures 3
sv_rcon_minfailuretime 30
sv_rcon_banpenalty 1440
With this, an address is banned for 24 hours after three failed attempts within 30 seconds; find sv_rcon shows which variables your build knows. The firewall rule from section 2 remains more effective, because it never lets the attempt reach the application. For access from changing connections, set up a local forwarding over SSH and talk to RCON on 127.0.0.1 afterwards:
ssh -N -L 27015:127.0.0.1:27015 root@YOUR.SERVER.IP.ADDRESS
6. Cap the rates and leave hibernation switched on
Team Fortress 2 runs at a fixed 66.67 ticks per second. How much traffic that becomes is not decided by the tick but by what a single client is allowed to request. Without an upper limit every player takes as much as the client asks for, and you pay for it with your outbound bandwidth:
sv_minrate 50000
sv_maxrate 100000
sv_mincmdrate 40
sv_maxcmdrate 66
sv_minupdaterate 40
sv_maxupdaterate 66
Do the math once: with sv_maxrate 100000 every player may pull 100 kilobytes per second, which across 24 slots is 2.4 megabytes per second, or roughly 19 Mbit/s outbound. Setting sv_maxrate 0 removes the cap entirely. League servers do that on purpose, a public server with many slots should not. Plugins that unlock the tick rate multiply the packet rate per player and therefore the same calculation.
The second point is frequently done wrong. TF2 goes to sleep as soon as nobody is connected and needs almost no CPU in that state. Many operators switch that off so the server feels "awake". On a machine with several instances this means the CPU is already busy while idle, and an attack hits a system that is full before it starts. Leave the defaults alone:
sv_hibernate_when_empty 1
sv_hibernate_postgame_delay 5
tf_allow_server_hibernation 1
7. Separate FastDL and switch off uploads
Community servers live on custom maps, and that creates a second attack surface. Without sv_downloadurl every player pulls the content through the game's own net channel, meaning through the same port and the same process that is simultaneously running the match. That amounts to a few kilobytes per second, one file after another, and with a 200 megabyte map collection it blocks your server for minutes per player:
sv_allowdownload 1
sv_allowupload 0
net_maxfilesize 64
sv_downloadurl "https://fastdl.example.org/tf/"
net_maxfilesize defaults to 15 and can be raised to 64 megabytes at most. sv_allowupload 0 stops clients from sending their own files (sprays, for example) to the server, and removes one of the few legitimate reasons for the split packets from section 4.
What matters is where the FastDL host sits. If it lives on the same IP address as the game server, an HTTP flood against 443/TCP is enough to fill the line and suffocate 27015/UDP with it. Put the fast download on a different host or behind a content network, and an attack on the files does not touch the game.
8. Limit the vote system, join floods and plugins
Not every outage is bandwidth. Because TF2 is free, an attack on the game logic costs nothing but accounts: join floods that occupy every slot, voice and chat spam, and abused votes that remove legitimate players. The TF2 defaults are already sensible here, but they are often loosened:
sv_allow_votes 1
sv_vote_issue_kick_allowed 0
sv_vote_allow_spectators 0
sv_vote_creation_timer 150
sv_vote_failure_timer 300
sv_vote_quorum_ratio 0.6
Those are the standard values: votes are allowed, kick votes are not, spectators do not vote, 150 seconds sit between two votes and 300 after a failed one, and a vote needs 60 percent approval. Anyone setting sv_vote_issue_kick_allowed 1 should know they are opening a tool that gets abused reliably on a public server.
Everything beyond that comes from SourceMod and Metamod:Source on TF2. Both live under tf/addons/ and report themselves in the console with meta version and sm version. Unlike on Counter-Strike 2, that foundation is mature here, and plugins for ban lists, join checks and chat limits are the usual route. Two rules go with it. Every plugin is code inside the same process, so a crashing plugin takes the server with it. And plugins that bring their own web services open additional ports and sometimes publish exactly the address you are trying to protect. sm plugins list shows what is actually running.
How seriously the engine side has to be taken was shown in April 2020: after older source code states of TF2 and CS:GO leaked, large community operators such as Creators.TF and Red Sun temporarily shut down their servers out of concern about exploitation. Keep the server binary current and the extensions matched to the engine version.
9. Measure and log before things go wrong
The most important step is the one almost nobody takes in advance: establish a baseline while everything is normal. Without a normal value you cannot say after an incident whether 40,000 packets per second was a lot or simply Friday evening. During an incident four commands are enough:
ip -s link show eth0
nstat -az | grep -i udp
tcpdump -ni eth0 -c 200 "udp port 27015 and udp[8:4] = 0xffffffff"
tcpdump -ni eth0 -c 200 "udp port 27015 and udp[8:4] = 0xfffffffe"
The first command shows packets, errors and drops per interface; run it twice ten seconds apart and you have a rate instead of an absolute value. The two captures separate the query flood from the split packet flood and therefore answer which of the rules from sections 3 and 4 is needed at all. Always limit them with -c, a capture under full load costs CPU time of its own.
Inside the server, the console command stats gives you CPU load, inbound and outbound network load in kilobytes per second, server FPS and the player count on a single line. If server FPS drops well below the tick value while the player count is normal, the server is busy with something other than the game. How to read those numbers is covered in Detecting a DDoS attack on your server.
Where these measures end: bandwidth and packet rate
Now the part no configuration file can solve. Everything described so far runs on your server, which is at the end of the line. A firewall rule decides about a packet that has already traveled down the cable. You can drop it, but you cannot make it unsent.
Put the normal operation of a full TF2 server next to a real attack and the ratio becomes obvious:
| Metric | Full TF2 server, 24 slots, 66.67 ticks | Attack |
|---|---|---|
| Inbound packets | around 1,600 per second (24 players times 66 commands) | several million per second |
| Inbound bandwidth | well below 2 Mbit/s | commonly 5 to 50 Gbit/s against community game servers |
| Outbound bandwidth | around 19 Mbit/s at sv_maxrate 100000 |
not the problem |
| A2S queries | a few per minute per listing service | several thousand per second |
| Physical ceiling | 1 Gbit/s carries about 1.49 million smallest packets per second | 10 Gbit/s carries about 14.88 million |
A typical game server sits on 1 Gbit/s, which is 125 megabytes per second, and the line is full as soon as somebody sends more. The second figure is the packet rate, and it usually hits earlier than bandwidth: every packet costs one trip through the network stack, even if it is dropped afterwards. An attack that does not even fill a third of your line can therefore still take your server down. Operators experience this as "utilization was not even high, and everything was gone anyway".
For a sense of the magnitudes that actually occur: among the attacks filtered in real time on KernelHost servers were a UDP flood against a game server at more than 112.2 Gbit/s with more than 8.7 million packets per second, and a multi vector attack against a voice server at more than 473.4 Gbit/s with more than 41.5 million packets per second. 473.4 Gbit/s is roughly 470 times a 1 Gbit/s uplink. There is no local setting for that.
The two common emergency brakes do not help either. Null routing takes the attacked IP address out of the network and ends the attack, but it ends your server too. A reactive redirection costs exactly the minutes during which the match is decided. The only thing that works is filtering that runs permanently in the network in front of the server.
What KernelHost puts up against attacks on TF2 servers
The permanent protection included in every server package
DDoS protection at KernelHost is built in two stages and permanently active from provisioning onwards, with nothing for you to order, switch on or configure:
- Stage 1: 17 Tbps of mitigation capacity in the global scrubbing network. Volumetric attacks are cleaned close to their source, long before they reach the data center.
- Stage 2: Arbor real time filtering with 3.2 Tbps in Frankfurt am Main. Directly in front of the server, protocol specific patterns are recognized and dropped, packet by packet.
Two properties matter for a TF2 server. The filtering runs permanently and does not have to react to an attack first, so there is no switchover window in which your players get dropped and your server falls out of the server browser. And no null routing is used: your IP address stays in the network, only the malicious packets are discarded. Which games and protocols are covered is listed in Game server DDoS protection in real time.
Advanced DDoS Protection for servers under constant fire
Some projects are not attacked occasionally but deliberately and over weeks, with changing patterns and always exactly at match time. For those cases there is Advanced DDoS Protection from €50.00 per month, PrePaid and with no minimum term. The difference is not more capacity, it is control:
- A dedicated protected IP from the Frankfurt core, which your server is moved to inside our network. Nothing has to be rebuilt on your side.
- Self managed protection rules per port and protocol in the customer area: you configure separately what is allowed on 27015/UDP, what on 27020/UDP and what on 27015/TCP, without writing a ticket.
- Changes take effect in real time, so you can adjust during a running attack instead of waiting for the match to end.
- A protection profile matched to the game, for Team Fortress 2 and the other Source titles as well as free TCP and UDP profiles for custom applications.
The offer is aimed at servers running at KernelHost. If your TF2 server currently sits elsewhere and gets attacked regularly, moving it here is the path to this protection.
The two stages compared
| Feature | Included permanent DDoS protection | Advanced DDoS Protection |
|---|---|---|
| Price | included in every server package at no extra cost | from €50.00 per month, PrePaid |
| Activation | active from provisioning, nothing to set up | order it, receive the protected IP, server gets moved over |
| Filtering capacity | 17 Tbps global scrubbing plus Arbor real time filtering with 3.2 Tbps in Frankfurt am Main | the same two stage filtering |
| IP address | your server's IP address | an additional dedicated protected IP |
| Rule set | automatic profiles, fine tuning by ticket | your own rules per port and protocol in the customer area |
| Changes | applied automatically | take effect in real time, including during an attack |
| Game profile | optimized profiles for common games, Team Fortress 2 included | profile selectable per port, also for modified servers |
| Null routing | no | no |
| Term | tied to the server package | PrePaid, no minimum term, no notice period, no setup fee |
For most TF2 community servers the included permanent protection together with a clean server configuration is enough. Advanced DDoS Protection is the answer to somebody taking it personally.
Common mistakes and how to fix them
"The server is running but no longer shows up in the server browser": check the Game Server Login Token first. TF2 servers need a token for the public listing, set with sv_setsteamaccount and created for app ID 440. Steam revokes tokens that have not been used for 30 days. A server that disappears after a longer break therefore often just needs a fresh token and is not under attack at all. Only after that do an overly low sv_max_queries_sec_global or a blunt firewall rule on 27015/UDP come into question.
"CPU is at 100 percent while the line is nearly empty": that is the typical picture of a query flood or a split packet flood. Look for lines containing NET_GetLong in the server log and use the two tcpdump lines from section 9 to measure which packet type is arriving.
"My nftables or iptables rules have no effect": three causes are common. The rule sits behind the UFW chains and is never reached (hence priority -10), it was gone after the last reboot, or the attack is volumetric and the rule works correctly on a line that is already full. Check with nft list ruleset whether the counters rise. If they stay at zero, the rule is not being reached.
"I changed the IP address and was offline again the next day": the attacker gets the new address from the same source as the old one. Your server publishes it itself as soon as it is listed again, and old DNS records plus Discord status bots do the rest. Changing addresses buys hours, not a solution.
"The server crashes reproducibly without the bandwidth standing out": usually not a DDoS attack but a plugin that does not match the engine version, or an outdated server binary. sm plugins list and a comparison of version states are faster here than any filter rule.
"The server responds sluggishly after being idle": that is hibernation, not a fault. It drops CPU load to nearly zero while nobody is connected, and that is exactly the state in which you want headroom.
"Somebody else is running admin commands on my server": not a DDoS attack but a compromised RCON access. Set a new password immediately, restrict the port to your own address, and keep in mind that the password travels in clear text.
"My previous provider blocked my IP address": that is null routing. The provider is protecting its own network with it, and for you the result is identical to a successful attack, usually for hours afterwards. When in doubt, ask whether traffic is filtered or null routed. The answer says more about your availability than any hardware specification.
In short
- A TF2 server needs exactly 27015/UDP open to the internet. RCON on 27015/TCP belongs restricted to your own address, and SourceTV on 27020/UDP is switched off with
-nohltvif you do not broadcast. - Game traffic and the A2S query share the same port. Blocking or rate limiting 27015/UDP across the board throws out your own players. The limit has to run between the packet types, identified by the first four bytes after the UDP header.
- Split packet floods with the header
FE FF FF FFcreate CPU load instead of bandwidth and appear in the log asNET_GetLong. A tight limit on that packet type is defensible on TF2. - Since "Meet Your Match", new players only find community servers through the server browser. Any measure that pushes you out of that list works like the attack itself.
- A full 24 slot server handles around 1,600 inbound packets per second. Attacks against community game servers commonly run at 5 to 50 Gbit/s and several million packets per second.
- 1 Gbit/s carries about 1.49 million smallest packets per second. Above that line only the network in front of the server decides, not a rule on the server.
- At KernelHost the two stage permanent protection is included in every server package at no extra cost and is active from provisioning, without null routing. Advanced DDoS Protection is added from €50.00 per month when you want to steer the per port rules yourself.
If your server already runs at KernelHost, the filtering is active without you doing anything. If you still notice something unusual, open a support ticket so the filter rules for your IP address can be adjusted. During a running attack you can also reach us through the WhatsApp emergency chat at +43 650 8209883. Give us four details right away: IP address, port, time frame in your time zone and what you see. That saves a round of questions, and that round counts while a match is running.
Frequently asked questions
My TF2 server is offline right now. How do I tell whether it is a DDoS attack?
Which ports do I have to leave open for a Team Fortress 2 server?
Can I simply block or rate limit port 27015?
What do lines containing NET_GetLong in the server log mean?
My server is running but no longer listed in the server browser. Am I under attack?
Does it help to change the IP address right now?
At what attack size can my TF2 server no longer cope on its own?
Does my server at KernelHost go offline during an attack?
Does DDoS protection at KernelHost cost extra, and when do I need Advanced DDoS Protection?
2026 KernelHost GmbH. All rights reserved. This guide is protected by copyright. Republishing it on other websites, in whole, in part or in edited form, is not permitted without our written consent. Quoting with a source credit and a link is expressly welcome.

