All Articles

How to Stop Exploiters From Abusing Your RemoteEvents

Exploiters can fire any RemoteEvent with any arguments. Learn the 4 server-side checks that keep your Roblox game safe.

You made a shop. The client tells the server "buy this item for 50 coins" and the server obliges. Then one day a player has 9 billion coins.

How? The exploiter fired your RemoteEvent with their own arguments.

Quick answer: The client is never trustworthy. Send intent ("I want to buy the Sword"), and let the server decide everything else: price, ownership, distance, cooldown.


The rule: exploiters control the client

Anything running on a player's machine can be modified. That includes every LocalScript and every value they send through a Remote. You can't stop them from firing it — you can only make the server ignore bad requests.


The bad way

-- ❌ The client decides the price and the reward
Remote.OnServerEvent:Connect(function(player, itemName, price)
    player.leaderstats.Coins.Value -= price
    giveItem(player, itemName)
end)

An exploiter sends price = -999999 and gets rich.


The good way: 4 checks

1. Validate types

Arguments can be anything: nil, tables, a 200MB string.

if typeof(itemName) ~= "string" then return end

2. Validate against server data

The server owns the item list and the prices. The client never sends a price.

local ITEMS = { Sword = 50, Shield = 80 }
 
local price = ITEMS[itemName]
if not price then return end

3. Validate the player's state

Can this player actually do this right now?

local coins = player.leaderstats.Coins
if coins.Value < price then return end

For actions in the world (opening a door, picking up an item), also check distance between the character and the target.

4. Rate limit

Even a valid request can be spammed thousands of times a second.

local lastFire = {}
 
local function onCooldown(player, seconds)
    local now = os.clock()
    if lastFire[player] and now - lastFire[player] < seconds then
        return true
    end
    lastFire[player] = now
    return false
end
 
Players.PlayerRemoving:Connect(function(player)
    lastFire[player] = nil
end)

Put it together

Remote.OnServerEvent:Connect(function(player, itemName)
    if onCooldown(player, 0.5) then return end
    if typeof(itemName) ~= "string" then return end
 
    local price = ITEMS[itemName]
    if not price then return end
 
    local coins = player.leaderstats.Coins
    if coins.Value < price then return end
 
    coins.Value -= price
    giveItem(player, itemName)
end)

The client can now say only one thing: "I'd like a Sword." Everything else is decided where exploiters can't reach.


Bonus: what NOT to do

  • ❌ Don't create a Remote like GiveCoins — even if you "hide" it, it can be fired.
  • ❌ Don't trust player arguments that the client sends. The first argument of OnServerEvent is the real sender; any other player reference from the client is just data.
  • ❌ Don't calculate damage, rewards or cooldowns on the client.
Share
Enjoyed this article?

Check out what I've built

From Roblox games with millions of visits to open-source developer tools — see everything on my portfolio.

View Portfolio