Roblox Server-Side Anti-Cheat: Stop Speed & Fly Hacks
TL;DR
There's no silver-bullet library. Every "drop-in anti-cheat" will be bypassed by a halfway-decent exploiter quickly. What actually works is understanding why cheats are hard to detect server-side, then layering targeted sanity checks that accumulate evidence before acting. This guide is that checklist.
Why Server-Side Detection Is Hard
Before writing a single line of detection code, you need to understand the core problem: by default, the client owns its own character's physics. The server trusts the position data the client sends.
As the Roblox Wiki on replication filtering explains, filtering works by discarding property changes, but physics replication is handled by internal engine code — not scripts. This is why WalkSpeed exploits can speed up a cheater's character on the client without the property changing detectably on the server.
As Quenty's network ownership breakdown notes, if a player has network ownership over their character, they can send bad positional data — and Roblox does not verify physics calculations.
The consequence: You cannot simply read
HumanoidRootPart.Positionand trust it. You must compare it over time against what is physically plausible.
The Core Strategy: Plausibility, Not Property Checks
The classic mistake is checking Humanoid.WalkSpeed > 16. An exploiter can hook the speed value and mask it — the property looks fine on the server while the character moves at 200 studs/sec.
The real signal is movement delta over time.
Every polling interval, compare:
- Where was the player last tick?
- Where are they now?
- How much time elapsed?
- What is the maximum plausible distance they could cover?
If actual_distance >> max_plausible_distance, you have a flag.
-- Simple plausibility helper
local function getMaxPlausibleDistance(humanoid, dt, tolerance)
tolerance = tolerance or 1.8
return humanoid.WalkSpeed * dt * tolerance
end
Always use your server-authoritative WalkSpeed value — the one your game assigned — not a value you read back from the client's character.
Checklist: What to Detect and How
✅ 1. Speed Hacks — Track horizontal displacement per tick
local lastPos = {}
local function watchPlayer(player, character)
local hrp = character:WaitForChild("HumanoidRootPart")
local humanoid = character:WaitForChild("Humanoid")
lastPos[player] = hrp.Position
task.spawn(function()
while character.Parent do
task.wait(0.2)
local curr = hrp.Position
local prev = lastPos[player]
if prev then
local hDist = Vector3.new(curr.X - prev.X, 0, curr.Z - prev.Z).Magnitude
local maxAllowed = humanoid.WalkSpeed * 0.2 * 2.0 -- 2× tolerance
if hDist > maxAllowed then
-- add strike, never instant-kick on first flag
warn(player.Name .. " speed flag | dist=" .. math.round(hDist))
end
end
lastPos[player] = curr
end
end)
end
False positive killers:
- Use a 1.5–2× tolerance multiplier — lag causes legitimate position spikes.
- Skip checks for ~1 second after any server-initiated
PivotTo()/ respawn. - Disable checks while the player is in a vehicle (track a server-side bool).
✅ 2. Fly Hacks — Sustained airtime with no downward velocity
Fly cheats keep the character permanently airborne. Check Humanoid.FloorMaterial: if it returns Enum.Material.Air for too many consecutive ticks and the character isn't actually falling, it's suspicious.
local airTicks = {}
task.spawn(function()
while true do
task.wait(0.25)
for _, player in ipairs(game.Players:GetPlayers()) do
local char = player.Character
if not char then continue end
local humanoid = char:FindFirstChildOfClass("Humanoid")
local hrp = char:FindFirstChild("HumanoidRootPart")
if not humanoid or not hrp then continue end
if humanoid.FloorMaterial ~= Enum.Material.Air then
airTicks[player] = 0
else
airTicks[player] = (airTicks[player] or 0) + 1
-- 3 seconds sustained air = suspicious
if airTicks[player] >= 12 then
local vy = hrp.AssemblyLinearVelocity.Y
if vy > -5 then -- not actually falling
warn(player.Name .. " fly flag")
airTicks[player] = 0
end
end
end
end
end
end)
Pitfalls: Ladders, moving platforms, and long-jump mechanics all produce Air floor material. Tune the tick threshold to your game — a flat RP map can use 8 ticks; a parkour game may need 20+.
✅ 3. Teleport Hacks — Single-tick displacement above any plausible max
If a player moves 500 studs in 0.1 seconds, no amount of lag explains that. But your own game's teleports will trigger this check too — a critical DevForum-documented pitfall. Fix it with a server-side exemption flag:
local teleportExempt = {}
local function serverTeleportPlayer(player, destination)
teleportExempt[player] = tick() -- mark as exempt for 1 second
if player.Character then
player.Character:PivotTo(destination)
end
end
local function isTeleportExempt(player)
local t = teleportExempt[player]
return t and (tick() - t) < 1.0
end
Hard threshold example: with WalkSpeed = 16 and a 0.1 s tick, max plausible travel is ~32 studs with 2× tolerance. Anything above 80–100 studs per tick is a hard flag regardless of ping.
✅ 4. WalkSpeed Tampering — Cross-check via Humanoid.Running
Even if the property is masked, the Humanoid.Running event fires with the actual movement speed — a cheap secondary signal:
humanoid.Running:Connect(function(speed)
if speed > humanoid.WalkSpeed + 8 then
warn(player.Name .. " Running mismatch: " .. speed)
end
end)
✅ 5. Gravity & NoClip
- Gravity: Loop-check
workspace.Gravityserver-side. If it changes from your baseline (default196.2), reset it immediately. - NoClip: Cast a
workspace:Raycast()between the player's previous and current positions. If the ray hits a solid part the player passed through, flag it.
The Strike System: Never Instant-Kick
This is the most critical architectural decision. Network lag, ping spikes, and your own game mechanics will produce false positives. Experienced developers on the DevForum consistently note that movement checks shouldn't result in bans due to network unreliability alone.
Use a weighted strike accumulator with decay:
| Event | Strikes |
|---|---|
| Minor speed flag (1 tick) | +1 |
| Large/sustained speed flag | +3 |
| Fly flag (12+ ticks) | +4 |
| Hard teleport (100+ studs/tick) | +5 |
| WalkSpeed mismatch | +2 |
- 10 strikes → log to Discord webhook
- 20 strikes → kick with message
- Repeat offenders across sessions →
DataStoreServiceban
Strikes should decay (e.g. −1 every 30 s of clean movement) so a one-off lag spike doesn't punish legit players.
Logging
Every flag should record: UserId, DisplayName, flag type, server position, and timestamp. Route this to a Discord webhook and persist repeat-offender counts via DataStoreService.
The Nuclear Option: Server-Authoritative Character Controllers
For competitive or economy-critical games, sanity checks are a cat-and-mouse game. The real solution is a server-authoritative character controller like Chickynoid.
Chickynoid trusts nothing from the client except input directions — the server runs the authoritative physics simulation and corrects the client on disagreement. Players literally cannot move-cheat because the server's character state is ground truth. The trade-off is serious engineering effort: it's not a drop-in replacement for Humanoid-based characters.
Common Pitfalls at a Glance
- Checking
WalkSpeedproperty alone — it can be masked client-side - No ping tolerance — lag spikes trigger legit player kicks
- Forgetting to exempt server-side teleports (respawn, cutscenes)
- Polling too frequently — 0.1–0.25 s intervals are sufficient
- Not accounting for vehicles/dashes — track game states server-side
- Instant-kick on first flag — always use a strike accumulator
- No logging — you need data to tune thresholds
FAQ
Q: Can SetNetworkOwner(nil) stop character physics cheats? Not for the player's own character — Roblox automatically reassigns ownership back. It works well for world props and NPC parts, but not for the character itself.
Q: Is a client-side anti-cheat worth adding? It's a deterrent for casual script kiddies but provides zero security against any real executor. Server-side is your only reliable layer.
Q: What about Roblox's built-in anti-cheat? The platform's system targets executor detection and ToS violations — not your custom game mechanics like a special WalkSpeed buff or teleport ability. Game-logic integrity is on you.
Building anti-cheat is iterative — your first version will have false positives. Log everything, watch your Discord webhook, and tune thresholds week by week. You can browse community scripts on GM Market as a reference baseline, but always audit and tune anything you use. Good luck. 🛡️