SevyDevy

Guide · System design

Bot Detection System Design: Signals, Risk Scores and Tiered Responses

29 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • Bot detection system design is building a pipeline that spots automated traffic, meaning scripts posing as visitors. It decides what to do with each request without annoying real people.
  • It uses four kinds of signals: IP reputation (how trustworthy the sender's network address looks), request rate, device and browser clues, and behaviour, such as form-filling speed.
  • A risk score adds those clues into one number. Thresholds then pick a tiered response: allow, throttle (slow down), challenge (ask for proof, such as a CAPTCHA puzzle) or block.
  • Fingerprinting means recognising a device from many small traits, like screen size and fonts. Guidance from the W3C, the web standards body, says users typically cannot clear one. So collect little, store only a scrambled hash and keep it briefly.
  • Beginners can stop after the tiered responses section. Engineers get our simulation of 10,000 made-up sessions: a per-address rate limit refused requests from 9% of shoppers on shared mobile addresses, while scores missed every human-paced script.

What goes wrong when bots hit your signup and login pages?

Scripts create fake accounts by the thousand, try leaked passwords on your login page and grab limited stock before real buyers can. The quick fixes, a CAPTCHA on every form or blocking busy IP addresses, mostly punish your real users.

Good bot detection system design works the other way round. It watches quietly, scores each visitor's risk and adds friction only where the evidence is strong. Friction means anything that slows a real person down, from a short delay to a puzzle.

Picture a common setup. A shopping site in India runs a festive sale with a ₹500 welcome coupon for each new account, capped at the first 50,000 signups. The setup is made up, but the attacks are real and well documented.

Within minutes, scripts start creating accounts to farm coupons, and other scripts test stolen passwords on the login page. Real shoppers, many on mobile data, arrive at the same time. Every rule you add must stop the first group without stopping the second.

OWASP, the open web security community, lists these patterns in its Automated Threats to Web Applications project. Our sale meets Account Creation (OAT-019), Credential Stuffing (OAT-008) and Scalping (OAT-005), which means grabbing limited goods by unfair methods.

The project focuses on abuse of functionality, not bugs. Every feature here works as designed; the problem is who, or what, is using it, and how fast.

Which words do you need before designing bot detection?

You need eight words. Each one is tied to the festive sale.

  • Bot: a program that sends web requests on its own. Some are welcome, like the search engine crawlers that index your sale page. Others abuse your signup form.
  • Session: one visitor's run of requests, tied together by a cookie or an account. Most decisions in this guide are made per session, not per single request.
  • Signal: one observable clue about a request or session, such as its IP address or how many seconds the signup form took.
  • Risk score: one number, 0 to 100 in this guide, that adds up the signals. Higher means more likely automated.
  • False positive: a real shopper treated as a bot. False negative: a bot treated as a shopper. Every threshold trades one for the other.
  • Token bucket: a rate limiter that holds a fixed number of tokens. Each request spends one, and tokens refill at a steady rate, so short bursts pass but floods do not.
  • Challenge: a step that asks the visitor for proof, such as a CAPTCHA, a one-time code or a privacy-preserving token.
  • Fingerprint: an identifier built from many small device and browser traits. Most traits alone are shared by many people, but together they can single out one device.

What is bot detection system design and how does the pipeline work?

Bot detection system design is a pipeline with three jobs: collect signals, turn them into a risk score, and map that score to a response. It runs on every sensitive request, such as signup, login, coupon redemption and checkout.

OWASP's Bot Management and Anti-Automation Cheat Sheet says it bluntly: a single control is brittle. It recommends three layers that add up, because a request can look human at one layer and fail at another.

  • Edge layer: the first servers a request reaches, often a CDN (content delivery network) or firewall. It checks IP reputation and applies basic rate limits.
  • Application layer: your own servers. They see sessions, device and browser clues, behaviour and hidden form traps.
  • Business-logic layer: rules about what accounts do, such as coupons redeemed per card or signups per delivery address. Some scripts are only caught here.

A layered bot detection pipeline for signup

Notice that no layer decides alone. Every layer feeds the scorer, and every decision is logged, so the team can find false positives and retune the weights.

Diagram connections:

  • Visitor or script to Edge checks (request)
  • Edge checks to App signal collector (edge signals)
  • App signal collector to Risk scorer (all signals)
  • Business-logic checks to Risk scorer (account signals)
  • Risk scorer to Response policy (score)
  • Response policy to Signup service (allow or throttle)
  • Response policy to Visitor or script (challenge or block)
  • Response policy to Decision log (log decision)
  • Decision log to Tuning dashboard (daily review)
  • Tuning dashboard to Risk scorer (new weights)
  • Visitor or script: Sends a signup or login request
  • Edge checks: IP reputation and per-address token bucket
  • App signal collector: Device, browser and behaviour clues
  • Business-logic checks: Coupons per card, signups per address
  • Risk scorer: Adds signal points into a 0 to 100 score
  • Response policy: Thresholds pick allow, throttle, challenge, block
  • Signup service: Serves allowed and throttled requests
  • Decision log: Score, signals and action per session
  • Tuning dashboard: False positives per user segment

An everyday picture helps. Think of the security guard at the gate of a large housing society during a busy festival week.

Residents with a car sticker are waved through: that is allow. A delivery rider arriving for the twentieth time in an hour is asked to wait: throttle. A stranger asking for a flat number gets a call made to that flat: challenge. A person caught stealing last week is turned away: block.

Where the picture breaks: the guard sees a face, which is hard to fake. Your server sees only what the client chooses to send, and one script can arrive through thousands of addresses at once. So no single clue can be trusted the way a face can.

Which signals tell a bot from a human?

No signal does it alone. Each of the four families catches some scripts, and each one also trips some real people, which is why the scorer combines them.

The four signal families, what each catches and which real people it trips. The false-positive column comes from the sources below and our simulation.
Signal familyExample clueWhat it catchesWhich real people it trips
IP reputationAddress belongs to a hosting provider or sits on an abuse listCrude scripts running on rented serversVPN users, whose traffic exits through a provider's servers, and addresses recycled after past abuse
Rate patternToo many requests per address, account or endpoint in a windowFloods and password-guessing runsMany mobile users who share one public address
Device and browsernavigator.webdriver is true, or claimed browser and observed features disagreeAutomation that announces itself, mismatched setupsPrivacy-hardened browsers and unusual devices
BehaviourForm submitted in under 3 seconds, or no keyboard and pointer eventsScripts that post the form directlyPassword-manager autofill users, and keyboard-only users for pointer checks

IP reputation and shared addresses

IP reputation asks what you know about the sender's address. Defenders usually treat addresses from cloud hosting ranges, or on lists of past abuse, as riskier than a home broadband line.

The catch is sharing. Internet providers, including mobile networks, can put many subscribers behind one public IPv4 address using carrier-grade NAT, or CGNAT. RFCs are the numbered documents that define internet standards. RFC 6598, from 2012, set aside address space for networks that run CGNAT.

RFC 6269, on issues with IP address sharing, warns that one user's failed logins may block out other users. It concludes that penalty box solutions to service abuse simply will not work under large-scale sharing.

Rate patterns need more than one key

A rate signal counts requests per key over time. The key is what you count by: address, session, account, endpoint or network owner. The cheat sheet recommends several keys at once, with the authenticated account as the most reliable once a user has logged in.

For login it suggests two separate buckets, one per username and one per address. A single combined bucket lets one address try the limit against an unlimited number of usernames.

It also says to avoid fixed-window counters, which allow bursts at window boundaries. Our fixed_window_burst.py test agreed: a 10-per-minute fixed window let 20 requests through in 1.9 seconds across a minute boundary. A matching token bucket let 10 through.

The SevyDevy guide on rate limiter system design covers token bucket and sliding window algorithms in depth. Here we only need the bucket as one signal among four.

Device and browser signals

The User-Agent header is the short text where a client names its browser and operating system. MDN, the main web developer reference, warns that browsers routinely pretend to be another browser, so it is a weak clue.

navigator.webdriver is a true or false value a page can read. MDN says it indicates whether the browser is controlled by automation. As of 28 October 2026, MDN lists it as true, for example, in Chrome started with the enable-automation flag or in headless mode, which runs with no visible window.

The W3C WebDriver draft defines it so co-operating browsers can tell a page they are automated, letting the page switch to other code paths. The draft itself calls it complementary to the Evil Bit, an April Fools' RFC where attackers label their own traffic.

So treat true as strong evidence and false as proof of nothing. The cheat sheet adds a privacy rule: use passive, network-level signals first. One example is how the client opens its TLS connection, the encryption behind HTTPS. Resort to client-side fingerprinting only when necessary.

Behaviour signals

Behaviour asks how the visitor moved through the flow. It checks whether the session loaded the signup page before posting the form, how many seconds that took, and whether any key presses or pointer movements happened.

These are strong against simple scripts, but they have known human failure modes. Password managers and browser autofill fill a form far faster than anyone types. Keyboard-only and screen-reader users never move a mouse, so a pointer-only check flags them.

How does a risk score turn signals into one number?

The simplest risk score is a points table: each signal that fires adds its weight, and the total is capped at 100. Thresholds on that total then pick the response tier.

Points tables are easy to explain to support teams and easy to audit. You can later swap the weights for a model trained on labelled outcomes. The thresholds and the false positive checks below stay the same.

This code is a cut-down version of our full simulation. It runs on Python 3.13.17 with only the standard library.

python
import bisect


class TokenBucket:
    def __init__(self, capacity, refill_per_sec):
        self.capacity = capacity
        self.rate = refill_per_sec
        self.tokens = float(capacity)
        self.last = 0.0

    def allow(self, now):
        elapsed = now - self.last
        self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
        self.last = now
        if int(self.tokens):          # at least one whole token left
            self.tokens -= 1
            return True
        return False


WEIGHTS = {"bad_ip": 35, "datacentre_ip": 20, "rate_hit": 15,
           "webdriver": 40, "inconsistent": 20, "fast_fill": 25, "no_input": 15}
CUTS = [30, 50, 75]                   # throttle, challenge and block start here
TIERS = ["allow", "throttle", "challenge", "block"]


def risk_score(signals):
    return min(100, sum(WEIGHTS[name] for name in signals))


def tier(score):
    return TIERS[bisect.bisect_right(CUTS, score)]


bucket = TokenBucket(capacity=20, refill_per_sec=0.2)
results = [bucket.allow(now=i * 0.5) for i in range(30)]
print("allowed", results.count(True), "of", len(results))

for signals in ([], ["datacentre_ip"], ["datacentre_ip", "fast_fill"],
                ["webdriver", "fast_fill"], ["bad_ip", "webdriver", "rate_hit"]):
    score = risk_score(signals)
    print(signals, score, tier(score))

print([tier(s) for s in (29, 30, 49, 50, 74, 75)])

The tier function uses bisect_right from Python's bisect module, which finds which band of the CUTS list a score falls into. A score of exactly 30, 50 or 75 lands in the higher tier.

Running it prints allowed 22 of 30, then five scored examples: 0 allow, 20 allow, 45 throttle, 65 challenge and 90 block. The last line checks the band edges: allow, throttle, throttle, challenge, challenge, block.

The bucket starts full with 20 tokens and earns back only one token every 5 seconds. So a request every half second runs dry after 22 requests.

Look at the second example. A datacentre address alone scores 20 and is allowed. That is deliberate: in our simulation, a challenge threshold of 20 would have challenged every one of 515 VPN users.

The design rule is that no single soft signal should reach the challenge threshold on its own. Only combinations should, so one misleading clue cannot hurt a real shopper.

How do tiered responses keep real users unbothered?

Tiered responses match the strength of the reaction to the strength of the evidence. Low scores pass untouched, middle scores get slowed or asked for proof, and only very high scores lose the action.

The cheat sheet's response table runs the same way. At low confidence it logs and serves normally. At medium confidence it asks for a step-up, such as a CAPTCHA or a multi-factor check. At very high confidence it blocks specific actions, like checkout, while still allowing browsing.

For confirmed abuse it suggests holding the account for manual review rather than deleting it, so the evidence survives. It argues for a graduated response, warning that hard blocks teach attackers what worked. For users with hardened browsers, it says to prefer challenge to block.

How a scored request picks its tier

Notice that a challenge is a second chance, not a punishment. A real shopper who passes it is allowed through, and rate limits still apply afterwards.

Diagram connections:

  • Scored signup request to Score 75 or more? (check top tier)
  • Score 75 or more? to Block the action (yes)
  • Score 75 or more? to Score 50 to 74? (no)
  • Score 50 to 74? to Challenge (yes)
  • Challenge to Challenge passed? (result)
  • Challenge passed? to Allow and log (yes)
  • Challenge passed? to Block the action (no)
  • Score 50 to 74? to Score 30 to 49? (no)
  • Score 30 to 49? to Throttle (yes)
  • Score 30 to 49? to Allow and log (no)
  • Scored signup request: Risk score from 0 to 100
  • Score 75 or more?: Several signals agree
  • Block the action: Refuse signup, keep browsing open
  • Score 50 to 74?: Some evidence, not enough to block
  • Challenge: Token, one-time code or CAPTCHA last
  • Challenge passed?: Checked on the server, single use
  • Score 30 to 49?: Weak evidence only
  • Throttle: Tighter rate limit, request still served
  • Allow and log: Serve normally, keep the score

Why the CAPTCHA comes last

The cheat sheet calls visible CAPTCHAs accessibility-hostile and says to treat them as a last-resort step-up, not a primary defence. OWASP even lists CAPTCHA Defeat as its own automated threat, OAT-009.

Lighter steps come first. A privacy-preserving token, covered below, can prove something about the client with no puzzle at all. A one-time code to a verified phone suits high-value actions. If you do show a puzzle, offer an accessible alternative, such as audio or a support contact.

What a throttled client should see

For a throttled request, HTTP has status code 429 Too Many Requests, defined in RFC 6585. The RFC says the response may include a Retry-After header saying how long to wait, and must not be stored by a cache.

For a public signup form, the cheat sheet advises a generic 429 that avoids a precise Retry-After and does not reveal which bucket fired. Precise feedback helps a script tune itself.

For partner APIs, the interfaces other companies' programs call, the trade-off flips, because well-behaved clients want to know their quota. The RateLimit header fields draft lets a server advertise its quota policy. As of 28 October 2026 it is still a draft at the IETF, the Internet Engineering Task Force that writes internet standards.

What did our 10,000-session simulation show about false positives?

It showed that the threshold moves errors around rather than removing them, and that some scripts are invisible to session signals. We built the pipeline above in Python 3.13.17 and ran it on synthetic traffic.

The traffic was 10 minutes of the festive sale: 9,000 human sessions and 1,000 scripted ones. Every number in the mix is made up, so the results show the shape of the trade-offs, not real-world rates.

  • Humans: 38% on shared mobile addresses, 6% on VPN or office addresses, 1% on recycled addresses that sit on an abuse list. 5% used autofill, 3% used only a keyboard and 3% had privacy-hardened browsers.
  • Crude scripts, half the bots: mostly datacentre addresses, fast regular requests, often announcing automation.
  • Moderate scripts, 35%: a mix of datacentre and home addresses, jittered timing, some mismatched signals.
  • Careful scripts, 15%: home and mobile addresses, human-like pacing, few mismatches. These stand for scripts that try hard to look ordinary.

Each address had a token bucket of 20 tokens refilling at 0.2 per second, or 12 a minute. Our full scripts are bot_sim.py and seeds.py; the cut-down code above shows their core.

Why per-address rate limiting alone hurts real users

Pause and predict: if the per-address token bucket were your only detector, which group would it hurt more, shoppers on shared mobile addresses or the moderate scripts? Guess before reading on.

The bucket gave at least one 429 to 9.09% of human sessions on shared mobile addresses, and to none on home broadband. Meanwhile 93.4% of moderate scripts and 97.6% of careful scripts never hit it.

Across all traffic, it denied 62.6% of bot requests, so it did slow the floods. But it missed half the scripted sessions overall, and nearly all of those misses were scripts that spread their requests out.

How the threshold trades friction for missed bots

Next we scored every session with the points table and acted, by challenge or block, on everything at or above a threshold T. Precision here means: of everyone we acted on, the share that really were bots.

Single-threshold sweep from bot_sim.py, seed 42: 9,000 made-up human sessions and 1,000 made-up scripted sessions.
Act when score is at leastHumans acted on (false positive rate)Bots not acted on (false negative rate)Precision
2012.59%19.60%41.51%
302.17%24.20%79.54%
400.90%36.30%88.72%
500.40%41.30%94.22%
600.12%46.20%98.00%
700.02%48.90%99.61%

Read the jump between 20 and 30. At 20, one soft signal could trigger action, so every VPN user and 60.7% of autofill users were caught. At 30, a datacentre address or fast filling could no longer trigger action alone, and the share of humans acted on fell from 12.59% to 2.17%.

Precision also shows the base-rate trap: when bots are rare, even a small false positive rate wrongly hits many people. Bots were 10% of sessions, so at T of 30 about one in five sessions we acted on was a real shopper.

With the tiers from the decision diagram, 30, 50 and 75, only 0.40% of human sessions saw a challenge or block, and 2 humans out of 9,000 were blocked outright. Another 1.77% were throttled. But 43.6% of bot sessions still got through, by being allowed, throttled or passing a challenge.

Across ten random seeds the picture held. The share of humans acted on at T of 50 ranged from 0.19% to 0.57%, and bots missed ranged from 39.0% to 43.7%.

Who the false positives were

At T of 50, 35 of the 36 humans we acted on sat on recycled addresses from the abuse list. All 99 shoppers on those addresses had done nothing wrong; their address had a past.

Dropping IP reputation from the score cut human false positives from 0.40% to 0.03%, but raised missed bots from 41.3% to 47.3%. That is the real trade: give list entries an expiry date and a lower weight, rather than deleting the signal.

What scores cannot see

As we built them, careful scripts trip almost none of our signals. So every one of the 170 scored under 50, and their median score was 0. A script that paces itself like a person, from an ordinary address, produces session signals that look like a person's.

Those are caught, if at all, at the business-logic layer: the hundredth account redeeming a coupon to the same delivery address, or the same payment card. The SevyDevy guide on fraud detection system design covers that kind of scoring.

What just happened? The rate limit caught floods but hurt shared addresses. The score cut challenges and blocks for humans to under half a per cent, though 1.77% were throttled, and it let careful scripts through. Each layer covered a gap the others left.

How far can browser fingerprinting go before it breaks privacy?

Only as far as a specific security need justifies, and no further. The W3C note Mitigating Browser Fingerprinting in Web Specifications, a Group Note dated 25 September 2025, explains why fingerprints are risky.

It defines browser fingerprinting as a site's capability to identify or re-identify a visiting user, user agent or device. It notes that a fingerprint typically cannot be cleared or re-set, unlike a cookie.

It separates two kinds. Passive fingerprinting uses only what arrives with the request, such as headers. Active fingerprinting runs code in the browser to read more traits, which the note says may be made detectable.

The note also says fingerprinting can be used as a security measure, for example to help authenticate a user. It calls its own recommendations mitigations, not solutions. Its advice to spec writers is to expose only the entropy, or identifying detail, a feature needs.

Turn that into rules for the festive sale, most of them from the OWASP cheat sheet's privacy guidance.

  • Prefer passive, network-level signals. Read active browser traits only on sensitive flows such as signup, login and checkout, never on every page view.
  • Hash or truncate any fingerprint before storage, and never keep raw values that allow re-identification.
  • Keep anti-bot signals for a short window, hours to days in the cheat sheet's words. Do not fingerprint low-risk traffic from logged-in users.
  • Say in your privacy notice that you collect device signals for security, and ask your privacy team whether consent is needed where your users live.
  • Never reuse security fingerprints for advertising or analytics. A clue collected to stop abuse should only stop abuse.

Privacy-hardened browsers, set up to reveal less about the device, are a false positive risk too. The cheat sheet says not to block users only because their browser is hardened. At T of 30, our simulation acted on 14.3% of hardened-browser users, more than any other group we tracked except recycled addresses.

How do Privacy Pass tokens cut CAPTCHAs without tracking users?

Privacy Pass lets a client prove it passed a check elsewhere, such as a device check, by showing a token your site cannot link back to that check. Three IETF RFCs, all from June 2024, define it. These tokens are signed passes, unrelated to the tokens in a token bucket.

  • RFC 9576, the architecture: names four roles. The Client wants access. The Attester checks some property of the client. The Issuer signs tokens. The Origin, your site, accepts them.
  • RFC 9577, the HTTP scheme: your server sends a challenge in the WWW-Authenticate header, HTTP's standard way to ask for credentials, using the PrivateToken scheme. The client answers in a new request with an Authorization header carrying the token.
  • RFC 9578, the issuance protocols: token type 0x0001 uses a VOPRF (verifiable oblivious pseudorandom function), checkable only with the issuer's private key. Type 0x0002 uses blind RSA signatures, checkable by anyone with the issuer's public key.

Both types are blind: the issuer signs or evaluates the token without seeing its final value. So it cannot later recognise the token when your site receives it, which makes redemption unlinkable.

One Privacy Pass token from challenge to redemption

Notice that the token tells the origin nothing about which client or attester it came from. It shows only that a trusted issuer signed it after a check.

Diagram connections:

  • Token challenge to Attestation (step 1 to 2)
  • Attestation to Blinded token request (step 2 to 3)
  • Blinded token request to Signed token response (step 3 to 4)
  • Signed token response to Token redemption (step 4 to 5)
  • Token redemption to Verify and allow (step 5 to 6)
  • Token challenge: Origin → Client: WWW-Authenticate PrivateToken
  • Attestation: Client → Attester: passes a check, e.g. device
  • Blinded token request: Client → Issuer, generally via the Attester
  • Signed token response: Issuer → Client: client unblinds the token
  • Token redemption: Client → Origin: Authorization PrivateToken
  • Verify and allow: Origin → Signup service: valid, unused token

RFC 9576 lists solving a CAPTCHA or checking device attestation as example checks. It describes tokens that attest the client is likely not a bot, and notes the original motivation was rate-limiting abusive traffic.

Apple's Private Access Tokens use this PrivateToken scheme. Apple's developer post of 9 June 2022 said iOS 16 and macOS Ventura support token type 2, and pitched them as a way to show CAPTCHAs less often.

The same post advises not blocking the main page load on a token challenge, so clients without token support can still use the site. That fits the tier design: a token is one more way to pass a challenge, not a gate.

Know the limits. A token proves only what its issuer's attester checked, not that a human is present. RFC 9576 also warns that Privacy Pass could enable discriminatory treatment of clients based on attestation support, so never punish a missing token.

How do you let good bots in without trusting a name?

Verify the crawler's network identity, never its User-Agent text. Your sale page wants search engine crawlers, uptime monitors and accessibility tools, which the cheat sheet calls legitimate. Partner integrations are welcome too.

As of 28 October 2026, Google documents two methods for its own search crawler. The manual one runs a reverse DNS lookup, which asks which name an address belongs to. The name must end in googlebot.com, google.com or googleusercontent.com.

Then a forward lookup on that name must return the same address. For large volumes, Google publishes its crawler address ranges as JSON files, such as common-crawlers.json. Keep one verified-crawler allowlist built from methods like these.

A file called robots.txt tells crawlers which paths to skip, but only polite crawlers obey it. The cheat sheet uses it as a trap: a path listed as forbidden that only abusive crawlers will visit.

What is coming: the IETF Web Bot Auth working group is drafting a way for bots to sign each request with HTTP Message Signatures, RFC 9421. As of 28 October 2026 the protocol is an Internet-Draft, so treat it as an extra signal, not a standard.

How do you measure false positives once bot detection is live?

You measure them indirectly, because live traffic has no labels saying who was a bot. You watch what flagged users do next, compare segments and keep a small group untouched as a baseline.

A challenged session that solves the challenge and then buys something was probably a person, though some scripts do pass challenges. A blocked session followed by a support ticket from a real customer is a confirmed false positive. Count both, per segment.

  1. Run in shadow mode first

    Score every session and log the tier it would get, but serve everyone normally. Two weeks of logs show what each threshold would have done.

  2. Break results down by segment

    Split the would-be challenges by network type, device, autofill use and accessibility settings. Our simulation's worst false positives hid inside small segments.

  3. Turn on the mildest tier

    Enable throttle only. Watch the signup-to-purchase funnel and support tickets for real shoppers who were slowed.

  4. Add challenges, then soft blocks

    Enable challenges at the agreed threshold, then blocks on specific actions such as coupon redemption. Keep browsing open for everyone.

  5. Keep a holdout

    Leave a small random share of flagged sessions unchallenged and logged. It gives you an unbiased view of what the score misses and whom it hurts.

  6. Alert on drift

    The cheat sheet suggests dashboards for request rates, login success and the signup-to-purchase funnel, treating shifts beyond 3 sigma, three standard deviations from normal, as possible bot activity.

To check it worked, track two numbers every day. Human challenge rate is challenged or blocked sessions that later proved human, per 10,000 human sessions. Bot pass-through rate is confirmed abusive accounts that were allowed, per 10,000 new accounts.

If the human challenge rate rises after a change while pass-through stays flat, roll the change back. A detector that bothers more people without stopping more bots is simply worse.

How do you explain bot detection system design in a system design interview?

Start from the abuse and the cost of errors, then build signals, score and tiers, and finish with how you would measure mistakes. Interviewers want trade-offs, not a vendor name.

  1. Name the flows and threats

    Signup, login, coupons and checkout. Map them to OWASP threats such as Account Creation, Credential Stuffing and Scalping.

  2. Define the two error costs

    A false positive loses a real customer. A false negative lets a fake account take a coupon. Agree which matters more for each flow.

  3. Lay out the signal families

    IP reputation and rate at the edge, device and behaviour in the app, account velocity in business logic. Give one human failure mode for each.

  4. Combine into a score

    Start with a points table so no single soft signal reaches the challenge threshold. Mention that a trained model can replace the weights later.

  5. Map scores to tiers

    Allow, throttle, challenge, block. Prefer a graduated response, block actions rather than whole sessions, and use a CAPTCHA last.

  6. Set privacy limits

    Passive signals first, hashed fingerprints, short retention, disclosure. Offer Privacy Pass tokens to cut puzzles.

  7. Explain the evaluation loop

    Shadow mode, segment breakdowns, a holdout and drift alerts. Quote human challenges per 10,000 human sessions as the number you defend.

Questions people ask

What is bot detection in simple words?
It is a system that decides whether a request comes from a person or a program, and how to respond. It gathers clues such as the address, request rate, browser traits and form-filling speed, adds them into a risk score, and then allows, slows, challenges or blocks. Good designs touch very few real people.
Is a CAPTCHA enough to stop bots?
No. The OWASP Bot Management cheat sheet calls visible CAPTCHAs accessibility-hostile and says to use them as a last-resort step-up, not a primary defence. OWASP also lists CAPTCHA Defeat as a separate automated threat, OAT-009. Use rate limits, risk scores and lighter challenges first, and offer an accessible alternative when a puzzle is unavoidable.
Should I block all traffic from datacentre IP addresses?
No. VPN users arrive from datacentre ranges because their traffic exits through the VPN provider's servers. In our made-up simulation, a threshold that let a datacentre address act alone challenged or blocked all 515 VPN users. Treat the address as one signal worth some points, and act only when other signals agree.
Is navigator.webdriver a reliable way to detect bots?
Only in one direction. The W3C WebDriver draft defines it so co-operating browsers can tell a page they are under automation, and the draft compares it to the Evil Bit joke RFC. A true value is strong evidence of automation. A false value proves nothing, so never treat it as a sign of a human.
Is browser fingerprinting allowed for bot detection?
It can be, within limits. The W3C fingerprinting note accepts that fingerprinting can serve security, while warning that users typically cannot clear a fingerprint. Collect only on sensitive flows, hash before storing, keep data briefly and disclose it. Rules differ by country, so ask your privacy team; this guide is not legal advice.
What false positive rate is acceptable for bot detection?
There is no universal number. It depends on what a lost customer costs against what a fake account costs on that flow. Measure it per segment, because an overall rate under half a per cent can hide one group, such as users on shared addresses, being hit far harder.
How is bot detection different from fraud detection?
Bot detection asks whether traffic is automated, across signup, login, scraping and checkout. Fraud detection asks whether a payment or transaction is dishonest, whoever made it. They share signals and scoring ideas. Scripts that look human at the session level can only be caught, if at all, by fraud-style checks on accounts and payments.

Sources and further reading

Practise this with Lala

Practise