Back to Blog
9 minfiveshield Team

FiveM Layer 7 Attacks: players.json, info.json and sv_requestParanoia

What an attacker gets from FXServer's HTTP endpoints, what each sv_requestParanoia level does, and the L7 vectors it does not cover.

fivem layer 7fivem server protectionfivem ddos attack

What an Attacker Gets by Calling /players.json

When people talk about DDoS on FiveM, everyone pictures a UDP flood on port 30120. It is the most visible vector, but it is almost never the first one tried — because it costs the attacker bandwidth. The first attempt is HTTP.

FXServer exposes three public endpoints on its game port: /info.json, /dynamic.json and /players.json. They exist for good reasons — the Cfx.re listing, monitoring tools and stats sites poll them constantly. But they have two properties that make them a perfect target:

  • They answer without authentication. Anyone, from anywhere, can call them.
  • Each request costs the server more than it costs the caller. FXServer has to serialize the current server state to answer. The HTTP request going out fits in a few hundred bytes.

That is the definition of an application-layer attack: the cost asymmetry, not the volume. A few thousand requests per second against /players.json are enough to push FXServer's CPU up and degrade tick for everyone, without ever approaching a bandwidth limit. In the captures owners send us, this is the scenario that most often ends with "CPU was at 100% but the network was quiet".

As a bonus for the attacker, these endpoints are reconnaissance: player count, loaded resources, build number, the sets variables your config publishes. Free intelligence ahead of the next attack.


sv_requestParanoia: What Each Level Actually Does

FiveM ships a built-in countermeasure, and it deserves to be far better known. sv_requestParanoia is an integer from 0 to 3, defaulting to 0, designed to counter proxy-based HTTP floods.

LevelDocumented behaviour
0Off — default behaviour
1Block any IPs sending requests containing a Via header
2Also block any IPs sending requests containing an Upgrade-Insecure-Requests header
3Also close the socket the requests have been submitted on

Two things are worth understanding precisely.

This is a response, not a socket block. When the check fails, FXServer answers a 403 whose body is literally Nope., on /info.json, /dynamic.json and /players.json. The TCP connection has therefore already been accepted and the request already read. The cost saved is the serialization of server state — real, but partial.

The trigger is a header, not a rate. Level 1 targets the Via header that HTTP proxies add; level 2 targets Upgrade-Insecure-Requests, which browsers send. That is exactly why level 2 breaks opening your endpoints in a browser: it is intended, a human has no business there. A monitoring tool that sends neither header keeps working.

The line to add to server.cfg:

set sv_requestParanoia 3

Level 3 is the right default on a public server: it adds the socket close, which is the part that costs the attacker most.


The Rest of the Hardening Block, and What It Is Worth

sv_requestParanoia rarely travels alone. Here are the convars that accompany it in shared configs, and what they really do according to the official documentation:

  • sv_forceIndirectListing true — boolean (false by default) that prevents the server from being advertised using its real IP. It is the documented origin-masking lever, covered in detail in our post on how to hide your FiveM server IP.
  • sv_authMinTrust — an integer from 1 to 5 (1 by default) expressing how unlikely it is for a user's identity to be spoofed by a malicious client. Raising it tightens the identity verification required to connect.
  • sv_scriptHookAllowed — a boolean, false by default; setting it to true lets clients with Script Hook V connect, which the documentation explicitly advises against.
  • sv_endpointPrivacy — historically it hid player IP addresses from the server's public reports. On current FXServer builds it has been removed: the behaviour became unconditional, and the server prints a startup message asking you to take it out of your configuration. That is not a loss, it is a protection that became permanent.
  • sv_useDirectListing — present in nearly every copy-pasted block, absent from both the server commands reference and the official proxy setup guide. Treat it as folklore.

That full block, correctly applied, is free and worth having on any public server. It still does not cover everything people call "Layer 7" on FiveM.


What sv_requestParanoia Does Not Cover: the cfx.re Connection Flood

The second application-layer vector is the harder one, and it is what takes down servers that thought they were hardened.

A player joining your server does not start by sending game traffic. They walk through FiveM's connection handshake: the join request, identity verification through Cfx.re, the deferral phase during which your resources can display a message or refuse, and then the transition to the game stream. That sequence has a cost — it occupies a connection slot, fires handlers in your resources, and waits on the network.

A connection attack simply runs it in a loop and never finishes it. The packets are well-formed. They come from distinct addresses. They look, byte for byte, like players trying to join — because that is exactly what they are, minus the intent.

sv_requestParanoia has no purchase on this: it inspects HTTP headers on three endpoints, and the connection flood does not go through them. A generic firewall has no more purchase, because there is nothing anomalous to filter at the packet level. Telling a real player from a fake connection takes a FiveM anti-DDoS that genuinely implements the cfx.re handshake and tracks per-session state — not an L4 tunnel that relays whatever it receives.


The Third Vector: the Resource Download Flood

The last one is the dumbest and the most effective. On first connection, a player downloads all of your resources. On a well-stocked RP server that runs from several hundred megabytes into the gigabytes.

So an attacker does not need a botnet: they just need to trigger that download over and over. Every iteration costs you real upstream bandwidth and saturates the machine's link long before the CPU flinches. That is why a CDN cache in front of your resources is a security measure and not a convenience — the details are in our post on why every FiveM server needs a CDN cache.

The three application-layer vectors side by side:

VectorWhat it targetsWhat native hardening does
HTTP endpoint flood/info.json, /dynamic.json, /players.jsonsv_requestParanoia 3 covers most of it
cfx.re connection floodThe connection handshakeNothing: the packets are valid
Download floodResources and FastDLNothing: the request is legitimate

Filtering L7 Upstream Instead of Inside FXServer

What the three rows have in common: by the time FXServer decides what to do, the packet has already arrived. The network link has been consumed, the kernel has done its work, the process has been woken. Native hardening reduces the cost of handling; it does not reduce the cost of receiving.

That is the structural difference with upstream filtering. A proxy that speaks the protocol can refuse the connection before it reaches your machine, and absorb the volume on a link sized for it. Upstream of your server is the only position from which you can both validate the handshake and avoid paying the bandwidth for what you reject — which is where FiveM DDoS protection with a cfx.re Layer 7 filter sits.

In practice, on our own servers, native hardening stays recommended — it costs nothing and it closes the HTTP endpoints — but it works behind the proxy rather than instead of it. The complete guide to stopping DDoS attacks on FiveM covers the architecture, and the install guide covers the server.cfg block to paste. Pricing is based on your daily peak player count.


Why a Lua "Anti-DDoS" Resource Cannot Do This

There are dozens of resources promising to block L7 attacks. The problem is position, not code quality.

A Lua resource runs inside FXServer. For it to see a request, the connection must have been accepted, the request must have been read, and the runtime must have been invoked. In other words: every cost the attack was trying to impose on you has already been paid by the time your code can react. The resource can decide not to answer, which saves the serialization — exactly what sv_requestParanoia already does natively, without a Lua runtime in the loop.

On the connection flood it is worse: a resource can only act from the deferral phase onward, i.e. after the handshake has already been engaged. On the download flood it sees nothing at all, because the fileserver does not route through it.

A resource can do useful things — log, enforce a join policy, ban an identified player. It cannot do mitigation, because mitigation is by definition about not receiving the traffic.


FAQ

Which sv_requestParanoia level should I use?

3 on a public server. It includes the level 1 and 2 blocks and adds the socket close. The only notable side effect is that you can no longer open /players.json in your browser — that is the intended behaviour.

Will it break my monitoring tools?

It depends on their headers. Level 1 rejects requests carrying a Via header, which HTTP proxies add; level 2 rejects Upgrade-Insecure-Requests, which browsers send. A client that sends neither — most command-line tools and monitoring agents — keeps working.

Why is my server lagging when bandwidth looks normal?

That is the signature of an application-layer attack rather than a volumetric one: CPU climbs, tick stretches, players time out together, and the traffic graph shows nothing dramatic. Check sv_requestParanoia first, then look at whether connection attempts are spiking. If the cause is still unclear, see how to tell whether it is really a DDoS.

Is sv_endpointPrivacy still useful?

On a recent build, no: the protection became unconditional and the convar was removed. The server will ask you to take it out of your configuration at startup. On an older build, leave it at true.

Is a firewall enough against L7 attacks?

No. A firewall decides based on addresses, ports and rates. All three vectors described here use valid requests from varied addresses to open ports. Deciding requires understanding the protocol, and that happens upstream of the machine, not on it.