Cloudflare Bot Management scoring: the 1-99 bot score and the signals behind it
Every request that passes through Cloudflare’s Bot Management gets a single number stapled to it: an integer from 1 to 99. One means the request is almost certainly a machine. Ninety-nine means it is almost certainly a person typing in a browser. A site owner never sees the model weights, the feature vector, or the dozen-plus internal detection IDs that produced the number. They see one field, cf.bot_management.score, and they write a firewall rule against it. That compression, from billions of features per request down to one byte of certainty, is the whole product.
So the interesting question is not “is this a bot.” It is: what does that single number actually summarise, how is it computed, and where does the compression lose information that a determined operator on either side of the fight can exploit? This post stays on the scoring machinery. Not the challenge platform that fires when the score is low, not the cf_clearance cookie that records the outcome, but the score itself and the engines that feed it.
The route below: first the score as customers consume it, the 1-99 range and the action thresholds it implies. Then the four detection engines that produce it, because the score is not one model but a precedence ladder of several. Then the verified-bots allowlist, which sits beside the score rather than inside it. Then how the number and its supporting fields are exposed at the edge, in WAF rules and in Workers. Then the parts that are not public, stated plainly as such, and a closing note on what the single-number design buys and what it costs.
The 1 to 99 score, and what each band means
The score is an integer in the closed range 1 to 99. Cloudflare’s documentation is blunt about the endpoints: a 1 means “Cloudflare is quite certain the request was automated,” and a 99 means “Cloudflare is quite certain the request came from a human.” There is no 0 and no 100 in the live signal; a value of 0 in tooling means the score was not computed for that request, not that the request scored maximally bot.
Cloudflare groups the range into bands that map onto recommended actions. A score of exactly 1 is “automated.” The band from 2 to 29 is “likely automated.” Everything from 30 to 99 is “likely human” and passes by default. The practical threshold that almost every Bot Management deployment ends up using is 30: below it you act, at or above it you let traffic through. The documentation’s own example logic challenges or blocks requests where the score is under 30 and the request is not a verified bot.
*The score collapses every detection signal into one integer. The action boundary sits at 30; below it Cloudflare recommends a challenge, above it traffic passes.*The score is monotone in one direction only as a guideline, not a guarantee. A 1 is meaningfully more certain than a 15, and a 15 is more certain than a 28, but the numbers are not calibrated probabilities you can multiply or average. Cloudflare warns against treating the score as continuous in that way and against, for example, averaging scores across a session to derive a verdict. The score belongs to a single request. Cloudflare does carry session context, through a Bot Management cookie that lets the system apply one visitor’s established request pattern to subsequent requests and stabilise their scores, but that is the platform smoothing the signal, not the customer doing arithmetic on it.
Granular per-request scores are an Enterprise Bot Management feature. Customers on lower tiers get the detection but consume it through coarser groupings in Bot Analytics, or through Super Bot Fight Mode’s likely-automated / likely-human / verified-bot buckets, rather than the raw integer. The raw cf.bot_management.score field in the Ruleset Engine is gated to Enterprise plans with Bot Management enabled. That gate matters for anyone reasoning about the product from the outside: the number a lot of public write-ups describe is one most sites never directly touch.
Four engines, one number: the detection ladder
The score is not the output of a single classifier. Bot Management runs several detection engines, and the score you see is the resolved output after they have all had their say. Cloudflare currently documents the engines as heuristics, machine learning, JavaScript detections, and an anomaly-detection engine that is now deprecated, with behavioral analysis folded into the machine-learning story. Each engine can claim a request, and they sit in a rough precedence order where a high-confidence engine overrides a lower-confidence one.
*The engines are not a committee that votes. They are a ladder. A decisive match high on the ladder ends the evaluation before the model runs.*Heuristics
The heuristics engine is the cheapest and most certain. It is a set of exact-match rules over request attributes, and when a request matches one, the engine assigns the lowest possible score of 1 with no model in the loop. The attributes are the obvious tells: software-library fingerprints (a default Go HTTP client, a known curl signature), specific HTTP request characteristics, TLS fingerprints, ASN data, and Cloudflare’s own threat intelligence. If your client announces itself as python-requests or carries the TLS fingerprint of an unpatched automation library, a heuristic catches it and the score is 1 before anything more expensive runs.
Cloudflare has put real engineering into making this engine flexible. The high-precision heuristics work, described in 2024, moved the rules into a Wireshark-inspired expression syntax serialised as YAML and deployed through the same Ruleset Engine that runs WAF rules. That lets Cloudflare’s analysts write and ship a new heuristic narrowly, against one precise combination of attributes, rather than painting with a broad brush that risks false positives. The same blog post puts a number on the engine’s reach: heuristics alone account for roughly 15 percent of global traffic and around 30 percent of Bot Management customers’ traffic being classified as bots. That is a large fraction of the verdict delivered by the simplest, most explainable engine in the stack, which is exactly where you want your high-confidence detections to live.
The heuristics engine also does double and triple duty. Beyond labelling traffic at runtime, its matches generate the labelled datasets that train the machine-learning models, and they serve as a benchmark against which new model candidates are validated before deployment. A request the heuristics catch with certainty today becomes a training example that teaches the model to catch its cousins tomorrow.
Machine learning
The machine-learning engine carries everything the heuristics do not decisively claim, which is most traffic. It is the engine that actually produces the spread of scores between the endpoints; the documentation notes it accounts for the majority of detections on Business and Enterprise plans. The model is a gradient-boosted decision-tree ensemble built on CatBoost, the open-source gradient-boosting library Cloudflare chose specifically for two properties: native support for high-cardinality categorical features, and inference fast enough to run at the edge. Cloudflare reported applying any of its models in under 50 microseconds per request, with the model exported through C and Rust APIs so it can run inside the LuaJIT-based edge stack.
The features are the request’s own attributes plus session and network context: headers and their ordering, session characteristics, browser signals, and global statistics that only a network proxying a large share of the web can compute. The training data is the leverage. Cloudflare trains on traffic measured in trillions of requests a week, drawing labelled examples from both free and paid zones, and the breadth of that corpus, across many sites and many attack shapes at once, is what lets a per-request classifier generalise to bots it has not seen before.
The model is versioned, and the versions move forward over time. The original, ML1, shipped around 2019 and classified a request purely from its attributes. By 2022 Cloudflare had launched several successor models, with a release that year aimed squarely at mobile-app traffic, where the absence of a normal browser environment had been producing false positives; one customer’s Android false-positive rate dropped to essentially zero after the update. The most consequential public milestone since is ML v8, deployed in June 2024 and built to catch bots hiding behind residential proxies. That problem is hard precisely because the IP address is a real residential device and carries no signal on its own; Cloudflare’s answer combined behavioral and latency-based features with new datasets to classify residential-proxy traffic per request. The numbers Cloudflare published give a sense of the scale the model operates at: it processes north of 46 million HTTP requests per second, and in any given hour v8 is flagging more than 17 million unique IPs taking part in residential-proxy attacks, spread across roughly 45,000 ASNs in 237 countries and territories. The detail that justifies the per-request approach: on those residential networks, about four out of five requests are benign connections from real devices, so blocking by IP or ASN would punish far more humans than bots.
Behavioral analysis
Behavioral analysis is the unsupervised counterpart to the supervised ML engine. Cloudflare developed it in 2018 and integrated it into the platform in 2019. Where the supervised model learns from labelled bot and human examples, behavioral analysis builds a per-site baseline of what normal request patterns look like and flags the outliers, which means it can catch a bot the labelled training set has never seen. It is user-agent agnostic by design, so spoofing a Chrome user-agent string does nothing to evade it. In the current documentation this capability is described as part of the machine-learning story and the standalone anomaly-detection engine that exposed the older unsupervised approach is deprecated and not onboarding new customers, with a caution that it suited neither SaaS nor API-heavy domains well. The behavioral idea survives; the early packaging of it is being retired.
JavaScript detections
The fourth engine runs in the visitor’s browser rather than at the edge. JavaScript Detections injects a small, invisible script into HTML page responses (not AJAX calls) and uses what the browser reports back to separate real browser environments from headless and instrumented ones. There is no CAPTCHA and no visible challenge; Cloudflare’s framing is that it does not collect user data or fingerprint the person, only the execution environment. The outcome is recorded in the cf_clearance cookie and surfaced through the field cf.bot_management.js_detection.passed, a boolean that reads true when the browser cleared the check and false when it did not.
One detail trips up a lot of people reading the docs for the first time. A false on js_detection.passed does not, by itself, block anything. The JS-detection result is a signal you act on with your own WAF rule; the engine populates the field, the customer decides the consequence. The injected code has a 15-minute lifespan and is re-injected before the session expires, so a long-lived session keeps refreshing its proof that a real browser is still on the other end. For the deeper mechanics of how that injected challenge and its tokens are orchestrated, the Cloudflare challenge platform and the managed-versus-JS challenge write-ups go further than scope allows here. What matters for scoring is that the JS-detection boolean is one more input the ladder can weigh, distinct from the score itself.
The verified-bots allowlist sits beside the score
Not every automated request is unwanted. Googlebot, Bingbot, uptime monitors, and a long list of legitimate crawlers are machines that site owners generally want to admit. Cloudflare handles them with a verified-bots allowlist that lives alongside the score rather than inside it. A verified bot still gets a bot score; a well-behaved crawler will usually score low, because it is automated and the engines correctly say so. The allowlist is the mechanism that lets you write “low score, but allow it anyway if it is on the list.”
That is why the canonical Bot Management rule is not “block score under 30.” It is “block score under 30 and not a verified bot.” The two fields are independent. cf.bot_management.score tells you how bot-like the request is; cf.bot_management.verified_bot (a boolean) and cf.verified_bot_category (a string for segmenting by crawler type and purpose) tell you whether Cloudflare recognises the automation as one of the known good actors. Combining them is the entire point.
How a bot earns its place on the list has changed. The long-standing method is network identity validation: Cloudflare confirms that traffic claiming to be Googlebot actually originates from Google’s address space, historically through reverse-DNS checks, ASN membership, and published IP ranges, and where those are unavailable, through internal data and machine learning to recognise legitimate good-bot IPs. There is operational housekeeping around it. An inactive verified-bot IP ages out after roughly 24 hours of silence, and to qualify in the first place a bot needs enough traffic for Cloudflare to find it in sampled data, with publicly documented behavior or user-agent format.
The newer method is cryptographic. Cloudflare has been pushing Web Bot Auth, an approach where a bot signs its requests with a key whose public half is published, so the network can verify the signature rather than infer trust from the source IP. The current verified-bots documentation lists Web Bot Auth alongside IP validation as the two ways a bot can be recognised. The surface this exposes in the rules language is the cf.bot_management.signed_agent boolean, which tells you whether a request carried a valid signature from a known signed agent. That field is the scoring-side fingerprint of a real shift: from “we recognise your address” to “you proved your identity,” which is harder to spoof and does not break when a crawler moves to new IP space. It also reframes the allowlist as something a new crawler can opt into deliberately, instead of waiting to be discovered in sampled traffic.
How the score reaches the edge
A score that nobody can act on is useless, so the last piece is exposure. Bot Management publishes a small set of fields into the Ruleset Engine, the same expression evaluator that backs WAF custom rules, rate limiting, and transform rules. The headline field is cf.bot_management.score, the 1-99 integer. Around it sits a cluster of companion fields that let a rule author reason about the request without re-deriving the signals.
A few of these earn comment. cf.bot_management.static_resource is a guard rail: it flags requests for things like images and other static assets, and Cloudflare protects those by default so that a blanket low-score rule does not accidentally challenge every image embedded in an email or page. The standard pattern is to exclude them with not cf.bot_management.static_resource in the rule expression. cf.bot_management.detection_ids is the most revealing of the set; it is the list of heuristic detection IDs that matched the request, which means a rule author can act on a specific reason rather than just a low number. The 2025 account-takeover heuristics, for instance, surface as identifiable detection IDs (two of them watch for anomalous spikes in login failures and login attempts) that a rule can match directly. The TLS fingerprint fields, ja3_hash and ja4, expose the same network-layer signals the heuristics engine consumes, so a customer can write their own rules on the JA3/JA4 fingerprint even where Cloudflare’s own heuristics did not fire.
The same data is available inside Cloudflare Workers under request.cf.botManagement, including the score and jsDetection.passed, which lets application code branch on the verdict instead of relying only on a WAF rule. That is the difference between a static block at the edge and logic that, say, serves a degraded experience to a mid-range score while reserving hard blocks for a score of 1. The score is the same; the granularity of response is whatever the customer wants to build.
A worked rule, in the rules language rather than anything runnable against a live target, makes the composition concrete:
(cf.bot_management.score lt 30 and not cf.bot_management.verified_bot and not cf.bot_management.static_resource)That is the shape of the default. Act on likely-automated traffic, spare the known-good crawlers, and do not trip over your own static assets. Everything more sophisticated, rate-limiting by score band, challenging a narrow detection-ID set, allowing a specific verified-bot category, is a variation on combining these fields.
What is not public, stated plainly
A reference is only honest if it marks its own edges. Several things about the scoring machinery are not documented, and guessing at them would be the opposite of useful.
The exact feature vector the ML model consumes is not public. Cloudflare describes the feature families (headers, session characteristics, browser signals, network statistics) but not the full list, the encodings, or the weights. The CatBoost choice and the sub-50-microsecond inference figure are documented; the model’s internals are not, and that is by design, since a published feature list is a published evasion checklist.
The precise mapping from engine outputs to a final integer is not documented either. We know heuristics emit a 1 on a high-confidence match, we know the ML engine spreads scores across the range, and we know the engines sit in a precedence order. What we do not have is the exact arbitration logic when two engines disagree, or the calibration curve that turns a model probability into the specific integer you see. Some third-party write-ups describe a 1-or-29 behavior for the heuristics engine, where 29 marks a lower-confidence heuristic detection; Cloudflare’s own current heuristics documentation states only the score of 1 for a heuristic match, so the 29 figure should be treated as a community observation rather than a documented value. Where the public docs and third-party reports diverge, this post follows the docs and flags the gap.
The verified-bots validation pipeline is documented in outline (IP validation plus Web Bot Auth) but not in mechanism. The 24-hour delisting window and the minimum-traffic threshold are stated; the exact reverse-DNS and ASN logic, and how the machine-learning fallback decides a borderline IP is a legitimate good bot, are not. And the internal detection IDs, while exposed as opaque integers through cf.bot_management.detection_ids, are mostly not mapped to public meanings; only a handful, like the account-takeover IDs, have documented semantics. Anything that reads a detection ID and claims to know precisely which heuristic it represents, beyond the documented few, is inferring.
For comparison, the score-centric designs at other vendors land in the same place from different directions. DataDome’s server-side scoring pipeline and Akamai’s path from sensor collection to a bot-score header both compress many signals into a verdict, and HUMAN’s collective signal network leans on the same cross-customer telemetry advantage Cloudflare gets from proxying a large share of the web. The single-number interface is an industry convention, not a Cloudflare invention. What differs is the volume of the corpus behind the number and how much of the machinery each vendor is willing to document.
Closing: one byte of certainty, and its blast radius
The bot score is a remarkable act of compression. Trillions of weekly requests, a gradient-boosted tree ensemble, an unsupervised baseline per site, a YAML rulebook of exact-match heuristics, a browser-side environment probe, and a cross-network reputation system all collapse into an integer a site owner compares against the number 30. That design is why Bot Management is usable at all. Most security teams do not want a feature vector; they want a dial.
The compression has a cost, and it is the same cost on both sides of the fight. Because the customer sees only the resolved score, they cannot tell why a request scored 15 rather than 8 without reading detection_ids, and most do not. Because an operator probing the system also sees only the resolved score, they learn the boundary by binary search and nothing about the gradient that produced it. The score hides its own reasoning from everyone, which is a defensive property right up until a single engine drifts and drags a whole band of legitimate traffic below 30, at which point the opacity that protected the model becomes the thing making the false positives hard to diagnose. The 2022 mobile-traffic fix and the 2024 residential-proxy model were both, in part, responses to exactly that failure mode: a score that was confidently wrong about a population the existing engines did not understand.
The most concrete thing to take away is the boundary itself. Almost every Cloudflare-protected site in the world treats a bot score below 30 as suspect and at or above 30 as fine, with a verified-bot exception bolted on. That one threshold, sitting on top of four engines and a decade of model versions, is where the entire apparatus touches production traffic. Move it by a few points and you change who gets in.
Sources & further reading
- Cloudflare (2024), Bot scores — the canonical definition of the 1-99 range, the score bands, and the Enterprise-only access to granular scores.
- Cloudflare (2024), Bot detection engines — the current list of engines (heuristics, ML, JS detections, deprecated anomaly detection) and the score each produces.
- Alex Bocharov, Cloudflare (2020), Cloudflare Bot Management: machine learning and more — the CatBoost model, sub-50-microsecond inference, the five detection mechanisms, and the ~15%/~30% heuristics figures.
- Cloudflare (2024), Improved Bot Management flexibility and visibility with new high-precision heuristics — the Wireshark-inspired YAML heuristics syntax, the score-1 match behavior, and heuristics as ML training and validation data.
- Cloudflare (2024), Using machine learning to detect bot attacks that leverage residential proxies — ML v8, behavioral and latency features, 46M requests/sec, 17M unique IPs/hour, 45k ASNs.
- Cloudflare (2022), Evolving our machine learning to stop mobile bots — ML1’s origin and the 2022 mobile-traffic model that cut false positives toward zero.
- Cloudflare (2024), Bot Management variables — the full set of
cf.bot_management.*fields and the Workersrequest.cf.botManagementobject. - Cloudflare (2024), cf.bot_management.score field reference — type, range, and interpretation of the score field in the Ruleset Engine.
- Cloudflare (2024), Verified bots — IP validation and Web Bot Auth as the two recognition methods, plus the 24-hour delisting behavior.
- Cloudflare (2024), Challenge bad bots (WAF use case) — the canonical score-plus-verified-bot rule pattern and the static-resource exclusion.
- USPTO (2024), Web bot detection via behavioral analysis and machine learning — a granted patent describing behavioral plus ML bot detection of the kind underpinning these engines.
Frequently asked questions
What does a Cloudflare bot score of 0 mean versus a score of 1?
A score of 1 is the lowest live value and means Cloudflare is quite certain the request was automated. A 0 is different: it does not appear in the live signal and indicates the score was not computed for that request, rather than that the request scored maximally bot. The usable range runs from 1 to 99, where 99 means Cloudflare is quite certain the request came from a human.
Why does the canonical Cloudflare WAF rule check verified_bot instead of just blocking a low score?
Bot score and verified-bot status are independent fields. A well-behaved crawler like Googlebot still scores low because it genuinely is automated, so a blanket block under 30 would catch known-good crawlers. The verified-bots allowlist sits beside the score, so the standard rule blocks requests under 30 only when they are not verified bots. Combining the score with the verified_bot boolean is the point of the design.
How do Cloudflare's four bot detection engines combine into a single score?
The engines are not a committee that votes; they form a precedence ladder. The heuristics engine runs first and is cheapest and most certain, assigning a score of 1 on an exact match without invoking the model. Where no engine fires decisively, the machine-learning model carries the request and produces the spread of scores between the endpoints. A decisive high-confidence match ends evaluation before the model runs.
Can you average Cloudflare bot scores across a session to get a verdict?
No. Cloudflare warns against treating the score as a continuous, calibrated probability you can multiply or average. A 1 is more certain than a 15, and a 15 more than a 28, but the numbers are guidelines, not values you do arithmetic on. The score belongs to a single request. Cloudflare itself smooths signals through a Bot Management cookie that stabilises a visitor's scores, but that is the platform's work, not the customer's.
Does a false js_detection.passed value block a request on its own?
No. A false on cf.bot_management.js_detection.passed does not block anything by itself. JavaScript Detections injects an invisible script into HTML responses and reports whether the browser environment cleared the check, populating the field as a signal. The customer decides the consequence by writing their own WAF rule against it. The injected code has a 15-minute lifespan and is re-injected before a session expires.
Further reading
DataDome's detection model: every signal it collects on the first request
Traces what DataDome evaluates on the very first request, before any JavaScript runs: the TLS/JA4 fingerprint, the HTTP/2 frame profile, the header set, and IP and ASN reputation, and how those signals stack into one decision.
·19 min readCloudflare's cf_clearance cookie: issuance, scope, and lifetime
A reference on Cloudflare's cf_clearance cookie: when a passed challenge issues it, what it is bound to, its zone scope and partitioned cross-site behaviour, its configurable lifetime, and why a stolen copy does not travel.
·18 min readCloudflare's managed challenge vs JS challenge vs interactive challenge
Traces Cloudflare's challenge taxonomy: the JS (non-interactive) challenge, the managed challenge, the deprecated interactive challenge, and the retired CAPTCHA, when each fires, what each measures, and how the clearance levels differ.
·21 min read