Why checking WalkSpeed on the server catches nobody
Exploiters change WalkSpeed and JumpPower on their own client, so the server never sees it. How to measure movement on the server instead.
What it looks like
A server script looks for cheating by reading a property: the humanoid's WalkSpeed, JumpPower, JumpHeight or HipHeight, Workspace.Gravity, or CanCollide on the character's parts. An exploiter changes those values on their own client, where the server never sees the change, so the check always passes.
Luau
if humanoid.WalkSpeed > 16 then player:Kick("Speed hacking") end
How to detect it
- Find server checks that compare a character or world property with a limit and then kick, ban, rubber-band or flag.
- Roblox's access control guide says instance properties are server-authoritative: most must be changed on the server to be seen by everyone. A speed or jump value set by the client stays on the client. The server reads whatever it last set.
- Its network ownership guide lists what a client controls instead: its character's position and movement, its physics, and animations. Exploiters set
WalkSpeedlocally, and the result shows up only as the character moving faster. - Checks that measure the effect, such as distance moved over time, height reached or passing through walls, see what the client did. Checks that read the setting don't.
- The same applies to a client-side check moved to the server unchanged. Look for
GetPropertyChangedSignal("WalkSpeed")and similar in server scripts.
What an exploiter could do
They raise their speed, jump height or gravity and turn off collisions on their client. The server's check reads the values it set and finds nothing wrong, so they move freely while the game reports that it has an anti-cheat.
How to fix it
- Measure what the character does on the server: its position over time, averaged to allow for latency, as Movement checks that punish on one reading describes.
- Check the result where it matters, such as reach before an interaction or the time taken to finish a course.
- Keep property reads only for values the server itself changes, such as making sure a speed boost the server applied has ended.
Vulnerable example
Vulnerable
local Players = game:GetService("Players") local maxSpeed = 16 local maxJump = 50 Players.PlayerAdded:Connect(function(player) player.CharacterAdded:Connect(function(character) local humanoid = character:WaitForChild("Humanoid") :: Humanoid humanoid:GetPropertyChangedSignal("WalkSpeed"):Connect(function() if humanoid.WalkSpeed > maxSpeed or humanoid.JumpPower > maxJump then player:Kick("Movement values changed.") end end) end) end)
Fixed example
Fixed
local Players = game:GetService("Players") local RunService = game:GetService("RunService") local maxSpeed = 16 local allowance = 1.5 local window = 2 local strikesBeforeFlag = 3 local strikes: { [Player]: number } = {} local lastSeen: { [Player]: { position: Vector3, at: number } } = {} RunService.Heartbeat:Connect(function() local now = os.clock() for _, player in Players:GetPlayers() do local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart") if not root or not root:IsA("BasePart") then lastSeen[player] = nil continue end local previous = lastSeen[player] if not previous then lastSeen[player] = { position = root.Position, at = now } elseif now - previous.at >= window then local flat = (root.Position - previous.position) * Vector3.new(1, 0, 1) if flat.Magnitude > maxSpeed * allowance * (now - previous.at) then strikes[player] = (strikes[player] or 0) + 1 if strikes[player] >= strikesBeforeFlag then warn(`Movement flagged for review: {player.UserId}`) strikes[player] = 0 end end lastSeen[player] = { position = root.Position, at = now } end end end) Players.PlayerRemoving:Connect(function(player) strikes[player] = nil lastSeen[player] = nil end)
Common false positives
- Reading
WalkSpeedon the server to apply or remove the game's own boosts isn't a cheat check. - Checks on properties the server alone controls and replicates, such as a tool's server-set attributes, are fine.
- Movement checks that measure position over time, like the fixed example, are the fix. Judge how they punish with Movement checks that punish on one reading.