Roblox Server Authority: stop cheaters with client prediction and rollback
Roblox Server Authority is live for every game: the server owns state, clients predict and roll back when wrong. How to turn it on, write code for it, and its limits.
- With Server Authority the server owns game state and clients only send input, so speedhacks and flyhacks fail by design
- Clients predict a few frames ahead; when the server disagrees they reset to its state and resimulate the remaining frames
- Turn it on with Workspace.AuthorityMode = Server, move core logic into RunService:BindToSimulation and read input through InputAction
- Use time(), not tick() or os.clock(), because simulation time has to rewind during resimulation
If your Roblox game lets each client move its own character, a cheat program can move it too: too fast, through walls, into the air, because the server trusts the position the client reports. Server Authority, released for all games on 9 July 2026, flips that trust. The server owns the game state, clients send only their inputs, and client prediction hides the latency. This article covers the mechanism, how to switch it on, how to write code that fits it, and the traps.
Facts checked against the Server Authority page on the Creator Hub, the Full Release post on the DevForum (9 July 2026) and the BindToSimulation release post (29 April 2026), as of October 2026.
If you are not sure which code runs where yet, read Roblox EP3: server and client first; this builds on it.
How it worked before, and why cheating was easy
In the classic Roblox model, a player’s character has network ownership on that player’s machine. The client computes its own physics and sends the result up. You press a key and the character moves instantly, but the server has to believe the result. Anti-cheat meant writing after-the-fact checks, such as measuring distance per second and snapping players back, which is fiddly and easy to get wrong.
Server Authority makes the server the single source of truth. Clients send inputs only (move forward, jump) and the server simulates the outcome. A cheat that edits speed on its own machine has no effect on the real state.
Prediction, misprediction, rollback
Waiting for the server on every key press would make a 100 ms player feel lag on every input, so the client predicts:
- Prediction: the client simulates several frames ahead of the last server-confirmed state, using its own inputs. How many depends on latency
- Compare: when the authoritative state for a frame arrives, the client compares it with what it predicted for that same frame
- Misprediction: they differ (latency changed, another player bumped you, server-only logic ran)
- Rollback + resimulation: the client resets to the server’s state and replays the frames up to now, reapplying its own inputs
The docs’ example: a client two frames ahead mispredicts frame 3, so it resimulates frames 4 to 6 before frame 7. The player may see the character jump to the correct spot.
The client may guess, but it never decides. On every misprediction the server’s state wins.
Worked example: what a rollback costs
The frames-ahead count and per-step cost below are estimates to make the point; the docs give no formula.
BindToSimulationatEnum.StepFrequency.Hz60→ one step = 1000 ÷ 60 ≈ 16.7 ms- A player with 100 ms round trip → roughly 100 ÷ 16.7 ≈ 6 frames ahead
- Assume our callback costs 1 ms per step
- One misprediction → 6 steps × 1 ms = 6 ms of resimulation inside one frame’s 16.7 ms budget
At 3 ms per step that becomes 18 ms, already over a frame, and low-end devices drop frames. The lesson: everything in BindToSimulation can run again. Keep only logic that decides the outcome there; move cosmetics (sounds, effects, decorative animation) to RenderStepped.
Turning it on
In Studio, open Workspace properties, Server Authority section, and set AuthorityMode to Server. The rest follows automatically:
| Property | Set to | What it means for you |
|---|---|---|
NextGenerationReplication | Enabled | the new replication system |
PlayerScriptsUseInputActionSystem | Enabled | PlayerScripts use InputAction |
SignalBehavior | Deferred | events fire at the next resumption point, not immediately |
UseFixedSimulation | Enabled | internal systems (Humanoid, animation, ControllerManager) run at a fixed rate |
StreamingEnabled | Enabled | parts of the map far from the player may not be loaded on the client |
Then save and publish. If your game did not use StreamingEnabled before, that is the biggest change: client code that grabs workspace.Map.Door directly must wait for it to stream in.
UseFixedSimulation is the same idea as a fixed timestep game loop: every machine advances time in equal steps, so results can be compared.
Writing code for the simulation
The pattern from the docs: core logic lives in a ModuleScript initialised on both server and client, inputs come from InputAction, your own state goes in attributes, and attributes on predicted instances are written only inside BindToSimulation.
The sketch below is a Boost button that drains stamina. Create an InputContext named GameplayContext with a Bool InputAction named Boost in ReplicatedStorage.
-- ServerScriptService/GiveInputContext (Script)
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
Players.PlayerAdded:Connect(function(player)
-- an InputContext must be a descendant of the Player
ReplicatedStorage.GameplayContext:Clone().Parent = player
end)
-- ReplicatedStorage/Simulation (ModuleScript)
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local MAX_STAMINA = 100
local DRAIN_PER_SEC = 40
local Simulation = {}
function Simulation.Initialize()
RunService:BindToSimulation(function(dt)
-- the server simulates everyone; a client predicts only its own character
local list = if RunService:IsServer() then Players:GetPlayers() else { Players.LocalPlayer }
for _, player in list do
local character = player.Character
local context = player:FindFirstChild("GameplayContext")
if not (character and context) then continue end
local boosting = context.Boost:GetState() == true
local stamina = character:GetAttribute("Stamina") or MAX_STAMINA
if boosting and stamina > 0 then
stamina = math.max(0, stamina - DRAIN_PER_SEC * dt) -- always clamp
end
character:SetAttribute("Stamina", stamina)
end
end, Enum.StepFrequency.Hz60)
end
return Simulation
Call require(ReplicatedStorage.Simulation).Initialize() from both a Script in ServerScriptService and a LocalScript in StarterPlayerScripts. The client gets an instant result, the server holds the real value, and if the attribute differs between the two, the engine rolls back for you.
This follows the pattern in the docs; before shipping, compare it with Roblox’s Racing, Soccer or Laser Tag server authority templates.
Rules to remember
- Time: use
time(), the fixed-step simulation time that rewinds and replays.tick(),os.time()andos.clock()are not synchronised and do not roll back - Input: every input that affects the simulation goes through
InputAction, neverUserInputService.InputBegan; validate values before use - Attributes: only the first 64 per instance replicate, names up to 50 characters, string values up to 50 characters
- APIs inside the simulation: only properties and methods labelled “Simulation Access”.
task.spawn()from a callback keeps those restrictions;task.defer()escapes them - Camera: Camera InputAction no longer synchronises; use
Player:GetCameraState(), which returnsCFrame,FieldOfViewandViewportSize
Common mistakes
- Playing sounds or effects in BindToSimulation. A rollback plays them again; move them to
RenderStepped - Timing cooldowns with
os.clock(). It does not rewind during resimulation, so cooldowns drift; usetime() - Treating RemoteEvents as part of the timeline. Their order relative to properties and attributes is not guaranteed, and a reply in the release thread reports them landing about 40-50 ms off. Use them for discrete events only
- Switching on and publishing straight away. Test with Studio’s Network Simulation at high latency and packet loss first
- Ignoring the early limits. The Full Release post lists eight actively playing tracks per Animator, and changing Tools during a misprediction may delete the Tool’s Weld on the client. Check the current known limitations before switching
Sources
FAQ
Will turning it on break an existing game?
It can. It also forces StreamingEnabled, Deferred signal behaviour and the InputAction system. Code that relies on UserInputService, or assumes the whole map is always loaded on the client, needs changes. Always try it in a copy of the place first.
Do I still validate RemoteEvents myself?
Yes. Server Authority covers what runs in the simulation. RemoteEvents for discrete actions such as purchases still need permission and value checks on the server, and they are not ordered with the simulation timeline.
Will it cost more server CPU?
Roblox says server utilisation will increase, by how much depending on how close your game already is to the limit. Measure in Studio before switching on in production.
Commands and settings were tested on sample systems. Try them on a test machine before production.