Bot Management Demo — Cheat Sheet

~15–20 min · Glance once, click, talk · Deeper details in Bot_Management_REFERENCE.docx

Pre-demo setup checklist

1. Open (~4 min)

These three points usually come up on their own. Raise them yourself first so it feels like a conversation, then roll into the demo.

"So before I pull anything up, let me set the table a little. When bots come up, most teams kind of shrug it off. They figure it's not really their problem. And honestly, most companies have no idea until they actually go look. But if you've got a login page, a pricing page, an API, anything like that, something automated is already poking at it. The real question was never whether bots are hitting you. It's whether you can look at your traffic right now and tell the bots from your real customers. Most teams can't, and that's the whole gap we close."

"Now usually right about here someone asks, okay, but doesn't the WAF already do this? Good question, and it's worth pulling apart. Think of it this way. The WAF is looking at what's inside a request and asking, is this thing trying to hurt me? Bot Management is looking at how the request behaves and asking, is a real person doing this, or is it a machine? Two totally different questions. A request can be perfectly clean, nothing nasty in it at all, and still be a bot quietly scraping your prices all day long. So these two aren't competing, they cover for each other."

"And every so often someone on Pro tells me they figure they're already good. And fair enough, Pro handles the easy stuff really well. The lazy scripts, the default user agents, the basic headless browsers, it'll catch all of that. Where it runs out of road is the smarter traffic. A bot dressed up to look like a real browser, one that keeps changing its fingerprint, or traffic coming through residential proxies so it looks like somebody sitting at home. If you've got anything valuable tucked behind a login, that's exactly the stuff that slips past Pro."

"So let me show you how we actually pull this off, because this is kind of the fun part. The old way to spot a bot was to check the User-Agent header. That's basically the little ID card the browser holds up to say, hey, I'm Chrome. Problem is, that ID card is trivially easy to fake. One line of code and a bot is happily telling you it's Chrome when it's nothing of the sort."

"So we threw that out. Instead, we score every single request from 1 to 99, based on signals an attacker can't just rewrite on a whim. A 1 is almost certainly a bot. A 99 is almost certainly a real person. And everything in between, you get to draw the line wherever you want and write the rules that fit your business."

"Alright, enough talking. Let me just show you."

2. Bot Analytics page (~4 min)

▶ CLICK: Security → Analytics → Bot analysis tab

a) Likely automated vs. Likely human counts

"Right at the top, traffic is split into 'Likely Human' and 'Likely Automated.' High-level view of every request that hit this site in the last 24 hours — already sorted so you can see how much is just background noise."

b) Detection sources — ML vs. Cloudflare service

"Check out the Bot Score Source. A huge chunk of scoring comes from a machine-learning model that's basically seen it all. Cloudflare sits in front of about 20% of the internet, so we spot a brand-new attack pattern the second it pops up anywhere — and protect your site instantly."

c) SWITCH TO EVENTS TAB — JA3 / JA4 Fingerprint cards

"If you want the 'how' behind the magic, look at these JA4 fingerprints. Think of it as a digital serial number for the software. Unique hash of how a visitor introduces themselves during the handshake — encryption style, signature, the whole thing. Even if a bot rotates its IP or lies about its name, its digital DNA stays the same."

3. Live proof — five requests, five distinct signals (~6 min)

"I'm going to send five requests to my own site from a terminal. All five will return the same page — the demo isn't about blocking things in the terminal, it's about the signals Cloudflare records on each request. After the requests, we'll flip to Security Analytics and I'll show you how each one produced a visibly different row. Detection is the hard part. Once you can detect the difference between these five clients, writing a rule to block, challenge, or route any of them is a two-minute job in the WAF."

→ Switch to terminal. Big font on projector. Run all five in quick succession — under 30 seconds total — so they group together in the analytics timeline.

The Ray ID is your receipt. Every command uses -sI to show only response headers instead of the full HTML body. The line you care about is cf-ray — paste any of those Ray IDs into Security Analytics to see the exact row for that request, including bot score, JA4, verified_bot status, and detection IDs. That's how you prove Cloudflare made the judgment, not the origin.
What you should expect in the terminal. All five commands probably return 200 OK from your demo host unless you've configured a WAF or Bot Fight Mode rule that acts on bot score. That's expected — this demo shows Cloudflare's detection capability, which is the valuable half. Enforcement is a rule the customer writes after they see the signals. Don't promise blocks in the terminal that you haven't configured on the host.

TEST 1 — Real browser (baseline) → analytics signal: bot score in the human range, JA4 identifies Chrome

Open in Chrome:  https://nginx.tarheel.us/?demo=t1
"Real browser. Real TLS handshake. Client Hints headers sent by Chrome automatically. In analytics this row should show a bot score in the human range, JA4 identifying Chrome, and the UA column matching the JA4. This is your 'what a human looks like' reference row. To grab the Ray ID from a real browser, open DevTools → Network tab → click the request → look at Response Headers for cf-ray."

TEST 2 — Honest curl (no deception) → analytics signal: low bot score, JA4 identifies curl

curl -sI "https://nginx.tarheel.us/?demo=t2" | grep -E "HTTP|cf-ray"
"Unmodified curl. User-Agent literally says curl/8.x. JA4 fingerprint says curl. In analytics: bot score in the bot range, JA4 identifies curl, UA matches JA4 — everything is consistent. This is the honest bot baseline. Grab the cf-ray value; you'll paste it into Analytics in Step 4."

TEST 3 — Lying curl (User-Agent says Chrome, TLS says curl) → analytics signal: UA/JA4 mismatch — same JA4 as Test 2

curl -sI -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \
  "https://nginx.tarheel.us/?demo=t3" | grep -E "HTTP|cf-ray"
"Same curl binary as Test 2. Same JA4. But the User-Agent now claims Chrome. This is the payoff row. In Analytics the UA column will say Chrome, the JA4 column will identify curl. Those two facts cannot both be true for a legitimate client — a real Chrome browser would produce a Chrome JA4. That's the deception, visible in one row. Same JA4 as Test 2 is the receipt: same client, one is being honest about what it is, one is lying."

TEST 4 — Fake Googlebot → analytics signal: verified_bot = false, UA claims Googlebot

curl -sI -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
  "https://nginx.tarheel.us/?demo=t4" | grep -E "HTTP|cf-ray"
"Curl is claiming to be Googlebot. In analytics, the User-Agent column will say Googlebot. The verified_bot column will say false. Cloudflare cross-checks Googlebot claims against Google's actual published IP ranges — this request is not from a Google IP, so it fails verification. A WAF that only reads User-Agent strings would treat this as Googlebot and let it through. Cloudflare labels it as an unverified claim, which is exactly the signal a customer needs to write a rule against. That rule is a two-minute job once you can see the signal."

TEST 5 — Empty User-Agent → analytics signal: blank UA column, distinct row shape

curl -sI -A "" "https://nginx.tarheel.us/?demo=t5" | grep -E "HTTP|cf-ray"
"Empty User-Agent. Almost no legitimate client sends this. In analytics, the UA column is literally blank — a visually distinct row shape that stands out against the other four. Look for a numeric detection ID here that didn't appear on the other tests; that's Cloudflare flagging the empty-UA condition specifically."

🛠️ Optional — Run all four curl tests at once and collect Ray IDs

Paste this whole block into your terminal. It runs Tests 2–5 and prints a labeled table of Ray IDs so you can walk to Analytics with the receipts in hand.

run_test() {
  LABEL="$1"; UA="$2"; TAG="$3"
  RAY=$(curl -sI -A "$UA" "https://nginx.tarheel.us/?demo=$TAG" | grep -i '^cf-ray:' | awk '{print $2}' | tr -d '\r')
  printf "%-20s  cf-ray=%s\n" "$LABEL" "$RAY"
}
run_test "Honest curl"        "curl/8.7.1" t2
run_test "Lying curl (Chrome)" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" t3
run_test "Fake Googlebot"     "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" t4
run_test "Empty UA"           "" t5

Output looks like:

Honest curl           cf-ray=a23d54749c8912c5-ORD
Lying curl (Chrome)   cf-ray=a23d54812fab12c5-ORD
Fake Googlebot        cf-ray=a23d548a3b6f12c5-ORD
Empty UA              cf-ray=a23d5490c78b12c5-ORD
PUNCHLINE:

"Five requests. Same URL, same 200 response, same body. But in the analytics dashboard, five visibly different rows — different bot scores, different verified-bot status, different UA-to-JA4 relationships, different detection IDs. That's the detection layer. Once you can see that difference, blocking, challenging, or routing any of those clients is a one-rule change in the WAF."

"The one that usually sells it: Test 2 and Test 3 have the same JA4 fingerprint. Same TLS stack, same client. But Test 3 says 'I am Chrome' in the User-Agent. When you see them side by side in the dashboard, the whole story fits on one screen — the client can lie in headers, it cannot lie in the TLS handshake without rewriting its TLS library."

4. Walk the dashboard — five rows, five stories (~4 min)

"Let me show you what Cloudflare recorded for those five requests. Each row tells a different piece of the story."

▶ CLICK: Security → Analytics → Bot analysis tab.

▶ FILTER: Paste a cf-ray value from the terminal output into the Ray ID filter, or filter by the query string tag demo=t2 through t5. Sort by time to see all five in order.

What to point at, per row:

"The JA4 fingerprint is what makes this real. Tests 2 and 3 came from the same client — same binary, same TLS handshake — but Test 3 tried to lie in the header layer. That deception is visible in the dashboard as two columns that disagree. All of this was computed before any WAF rule ran. It's metadata on every request, available in Logpush, queryable in Analytics, usable in any custom rule."
Honest caveats on the numeric detection IDs. Cloudflare doesn't publish which detection ID number means what — that's deliberate policy so attackers can't reverse-engineer the heuristics. The story is that different rows fire different IDs, not that any specific ID has a named meaning. If a customer asks "what does detection ID 50331656 mean?", the honest answer is that mapping is available in an NDA session with a Bots product manager, arranged through their account team. Also, on a low-traffic demo host, some detections may not trip at all — pivot to the JA4 column and the verified_bot column, which will always show the story regardless of which specific detection IDs fired.

5. Block by JA4 — the payoff (~3 min)

"Here's what most teams do today — a bot misbehaves, they look at the IP, they block the IP. Bot rotates IPs, they block more IPs. Whack-a-mole, forever."

"Watch what we do instead."

▶ CLICK: Security → WAF → Custom Rules → Create Rule

Set:

"You're not blocking an IP. You're blocking the bot's engine. Same bot tomorrow on a different IP, different VPN — still blocked. They'd have to rewrite their TLS stack to change the fingerprint."

"That's the difference. IPs are license plates. JA4 is the engine serial number."

If asked: "Why Cloudflare and not Akamai / DataDome / PerimeterX / Imperva?"

  1. Network advantage — Cloudflare sees ~20% of internet traffic. The ML model is trained on a data set no competitor has.
  2. Same edge as everything else — no separate inline agent, no traffic mirroring, no sidecar. If you're on Cloudflare, scoring is already running.
  3. TLS fingerprinting is structural — JA3/JA4 can't be spoofed without rewriting the client's TLS stack. Strongest single signal in bot detection today.
  4. Verified bot list is cryptographic — no false-positives on Googlebot. Competitors often rely on User-Agent matching, which is trivial to spoof.
  5. One platform, one rule language — combine bot score with WAF, Rate Limiting, Access, API Shield. Not five vendors stitched together.

6. Close (~1 min)

"If you're already on Cloudflare, bot scoring is running right now — on every request hitting your zones. The only thing left is writing the rules that act on it."

Quick answers (if asked)

Will it block Googlebot?
No — verified bots are allowed automatically. Cryptographically verified, not header-based.

How accurate is the score?
Trained on ~20% of internet traffic. 1–30 = almost certainly bot. 70–99 = almost certainly human. 30–70 is the gray zone — use Managed Challenge.

Can attackers fake JA4?
Theoretically, by rewriting their TLS stack. In practice almost nobody does — it's hard, breaks easily, attackers find easier targets.

What about headless Chrome / Puppeteer?
Distinct fingerprints. Headless Chrome has missing browser APIs and a different TLS fingerprint than regular Chrome. We catch all major headless frameworks.

Does this work for APIs?
Yes — bot score works on any HTTP request. API Shield adds API-specific protections (schema validation, sequence checks, mTLS) on top.

How much?
Bot Fight Mode: free. Super Bot Fight Mode: included on Pro/Business. Full Bot Management: Enterprise add-on.

How long to deploy?
If already on Cloudflare — minutes. Scoring is already running, you just write rules. From scratch, a few hours to a day.

Will it slow down my site?
No. Scoring runs in the same pipeline as the WAF — single-digit milliseconds. No detour, no extra hops.

What's the false positive rate?
Industry-low. The model is conservative — it'd rather let a borderline request through than block a real customer. Most false positives are legitimate automation (uptime monitors, your own scripts) — allowlist by IP or fingerprint.

How is this different from a CAPTCHA?
Bot Management is invisible — most users never see anything. CAPTCHAs interrupt every user. When we do challenge, we use Turnstile (invisible CAPTCHA replacement), not picking out fire hydrants.

How do I tune it without breaking anything?
Start in log-only mode. Watch the score distribution for a week. Identify your own automation (monitoring, partners). Allowlist them. Then move to enforcement.

Does it work with Workers?
Yes — Workers can read cf.bot_management.score and react. Useful for custom block pages, dynamic responses, API logic.

What about logged-in users?
Bot score still applies, but session signals are factored in. A user logged in and active for 20 minutes is treated differently from a fresh low-score request to /login.

How do I block AI scrapers (OpenAI, Anthropic, Perplexity)?
AI Crawl Control — separate from regular Bot Management. Cloudflare maintains a list of identified AI crawlers. Allow, Block, Challenge, or Charge per AI bot. Available on all plans including Free.