Engineering
Rate limiting server events in FiveM
Protect FiveM servers from event spam: per-player cooldowns, a reusable token-bucket limiter, limiting expensive callbacks and database work, cleaning up on playerDropped, logging abusers, and the built-in rate limiter convars available on FiveM for GTAV Enhanced.
Overview
Even a perfectly validated event can hurt you if it is called a thousand times a second: database queries pile up, logs explode and the server thread stalls. Rate limiting is the second half of event security — decide how often each player may do each thing, and refuse the rest.
A simple cooldown
local last = {}
local function cooldown(src, key, ms)
local now = GetGameTimer()
local k = src .. ':' .. key
if last[k] and now - last[k] < ms then return false end
last[k] = now
return true
end
RegisterNetEvent('fishing:sell', function()
local src = source
if not cooldown(src, 'sell', 3000) then return end
-- validate and pay
end)A token bucket
local buckets = {}
--- allow `burst` calls at once, refilling `rate` per second
function Allow(src, key, rate, burst)
local now = GetGameTimer() / 1000
local k = src .. ':' .. key
local b = buckets[k] or { tokens = burst, at = now }
b.tokens = math.min(burst, b.tokens + (now - b.at) * rate)
b.at = now
buckets[k] = b
if b.tokens < 1 then return false end
b.tokens = b.tokens - 1
return true
end
AddEventHandler('playerDropped', function()
local prefix = source .. ':'
for k in pairs(buckets) do
if k:sub(1, #prefix) == prefix then buckets[k] = nil end
end
end)Allow(src, 'chat', 2, 5) lets a player send five messages quickly, then two per second. Good for chat, UI actions and search boxes.
Callbacks and database work
Callbacks that query the database are a favourite spam target. Apply the same limiter at the top of the callback and cache results for a few seconds. ox_lib’s client-side delay argument helps honest clients, but only the server-side check stops abuse — see callbacks.
Built-in limits
FXServer applies its own network limits. On FiveM for GTAV Enhanced they are configurable with rateLimiter_<name>_rate and rateLimiter_<name>_burst convars (for example netEvent, stateBag). These protect the server as a whole; per-action limits in your scripts are still needed.
Logging abuse
Count limit hits per player. A few are normal (double clicks); hundreds in a minute are a script or a cheat. Send those to your staff logs — see Discord webhook logging and anticheat basics.
Frequently asked questions
How do I stop event spam in FiveM?
Add per-player limits in your server event handlers — a cooldown or token bucket — and refuse calls over the limit.
Is a client-side cooldown enough?
No. Cheaters call events directly; the limit must be on the server.
Should I clear limiter data?
Yes, on playerDropped, so the tables do not grow forever.
Does FiveM have built-in rate limits?
Yes for network traffic; on FiveM for GTAV Enhanced they are tunable with rateLimiter_* convars. Script-level limits are still needed.
Need this built, not just explained?
Ten years of FiveM work, from Lua to NUI
Custom resources, React NUI, ESX / QBCore / Qbox integration, OneSync performance audits and security reviews — plus the websites and SEO around your server brand.
Related guides
- EngineeringWriting server events that cannot be abusedAny client can call any event you registered with RegisterNetEvent, with any arguments. In each handler: copy source into a local, check argument types and ranges, re-derive everything from server state (prices, amounts, rewards), verify the player can do this now (distance, job, item, cooldown), and log refusals. Events only other server scripts should use are registered with AddEventHandler alone, so clients cannot trigger them.
- EngineeringProtecting a FiveM server against cheatersBuild in layers: make the server the authority for money, items and entities; validate every net event; enable sv_entityLockdown once scripts spawn server-side; filter dangerous game events (explosionEvent, weaponDamageEvent, clearPedTasksEvent) on the server; ban on several identifiers plus GetPlayerToken tokens; add honeypot events; and log everything. A commercial anticheat adds client-side detection on top — it does not replace the rest.
- FrameworksCallbacks: asking the server a question and getting an answerA callback registers a named handler on one side and lets the other side call it and receive its return value. With ox_lib use lib.callback.register on the server and lib.callback.await on the client (it also works server → client). ESX uses ESX.RegisterServerCallback / ESX.TriggerServerCallback, QBCore QBCore.Functions.CreateCallback / QBCore.Functions.TriggerCallback. Validate inside callbacks exactly like events.
- EngineeringKeeping your database safe from SQL injectionNever concatenate or format player-controlled values into SQL. Use oxmysql placeholders — ? or named @name parameters — and pass values separately, so they are always treated as data. Table and column names cannot be parameters: pick them from a fixed whitelist. Validate types (numbers are numbers), escape % and _ in LIKE searches, and give the server’s database user only the rights it needs.