Build1 publisher3 min readPublished
Cloudflare's per-IP counter hands a whole carrier NAT pool one shared budget
A Cloudflare walkthrough lays out how a rule set at 20 requests per 10 seconds, tuned against a handful of testers, locks out players sharing one carrier address. The counting key that avoids it is gated to Enterprise.
The Engineer · Build desk

What happened
- A dev.to walkthrough describes the common Cloudflare rule as blocking more than 20 requests per 10 seconds from one IP, with the threshold tuned against a staging environment holding a handful of testers.
- On launch night, several hundred players sharing one carrier-grade NAT address trip that counter, and Cloudflare returns a 429 or a challenge to every one of them.
- The post's replacement rule counts on cf.colo.id, ip.src and an x-player-session header at 15 requests per 10 seconds, with a Block action and a 60-second mitigation timeout.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- cost The counting key is set by plan: NAT-aware counting starts at Business and header counting at Enterprise, so the on-call engineer cannot configure a way out of the lockout on a Pro zone.
- decision Any team running a VPN egress, a proxy or a second CDN in front of Cloudflare has to choose between that hop and per-client rate limiting, because the edge will only ever count the hop's address.
- capability Adding a session-specific characteristic to the key makes it possible to block one abusive caller inside a shared carrier address while the other players behind it keep connecting.
- constraint The post offers the Log-action dry run only as an Enterprise path, which leaves cheaper zones learning what a threshold does from Security Events after it has already blocked someone.
A Rate Limiting Rule has two parts: a matching expression that picks which requests the rule applies to, and a list of characteristics that defines the counting key [7]. Key on ip.src alone and every request from that address increments one number. Twenty requests per ten seconds is two requests a second [1]. Everyone the carrier put behind that address shares those two requests a second.
Read "several hundred players" as three hundred, each sending one join call inside the window. The counter reads 300 against a budget of 20, fifteen times the limit [2]. None of that traffic is abusive, and the rule has no field that would show it. Cloudflare answers with a 429 or a challenge, and an entire region's worth of legitimate players gets treated as one abusive client [4]. The post says support tickets "can pile up faster than the on-call engineer can read Cloudflare's analytics dashboard" [5].
The characteristic that fixes this is gated by plan. Free and Pro count by IP only. Business adds the "IP with NAT support" characteristic. Counting by request header, cookie or query value requires Enterprise with Advanced Rate Limiting [6]. The replacement rule the post publishes counts on cf.colo.id, ip.src and http.request.headers["x-player-session"] [10], so it deploys only on Enterprise [4].
Check which address Cloudflare sees before writing any threshold [9]. It counts the client IP as seen at its own edge [8]. A load balancer at your origin, behind Cloudflare, changes nothing about the count. A proxy, a VPN or another CDN in front of Cloudflare replaces the client address with that hop's egress address, and every player behind it becomes one counted entity [8].
The published threshold is 15 requests per 10 seconds, which is 1.5 a second for each key [3] and 25 percent below the per-key allowance in the rule it replaces [5]. That number holds only if the client attaches the session header to every call on the matched path, one value per player, with no retry loop that exceeds a call and a half a second. The header is client-generated [12], so a caller who rotates it mints a fresh counter on each rotation. Keeping ip.src in the key does not bound that, because a new header value is a new key.
Game servers are a common DDoS target and an unprotected matchmaking endpoint is a real risk, the post says [13]. What it proposes is verification: Logpush or the Security Events view, so rule impact is checked against real traffic and not guesses [14], and on Enterprise a deployment with the Log action before the switch to Block [15]. The write-up is a configuration walkthrough and does not report traffic figures from a named outage [16]. It describes the shared-IP lockout as "a well-known failure mode, not a rare edge case" [17].
What to watch
- Whether Cloudflare moves NAT-aware counting below the Business plan. The fix would then not require a plan upgrade.
- Whether anyone publishes Security Events or Logpush data from a real launch-night lockout. That data would make this walkthrough a measured incident report.
- Whether a rule keyed on a client-supplied session header holds up against a caller that rotates the header to mint new counters.