Mitigation is the action layer. Bot detection identifies what is automated, bot management decides what should happen, and bot mitigation is the code that does it. This article covers the mitigation patterns that work in production: per-route recipes, sealed server-side decisioning, why proof-of-work has replaced CAPTCHA for most cases, and how to keep mitigation cheap for real users.
What mitigation should optimize for
A bot mitigation system is judged on four things:
- Catch rate. How much of the bad traffic does it stop?
- False positive rate. How many real users does it hurt while doing so?
- Latency. How much does the mitigation add to the median real-user request?
- Operability. How long does it take to update a rule, audit a decision, or roll back a mistake?
Most homegrown mitigations score well on (1) and badly on the other three. Most commercial mitigations score well on (1), (3), and sometimes (4), but get used so aggressively that they fail (2) silently. The hardest part of the work is finding the balance, route by route.
Mitigation recipes by route
Different routes have different threat models. The same bot management policy table belongs in different shapes per endpoint.
Sign-up
The threat: fake account creation, free tier abuse, identity farming. The friction budget is high for a first-time visitor but the false-positive cost is also high because a blocked signup is a lost customer.
Working recipe:
- Collect device fingerprint and behavioral snapshot during the signup form interaction.
- Score server-side at the moment of submit.
- For high-confidence human + clean device: allow. No friction.
- For ambiguous: email verification (existing pattern, low friction, useful corroboration).
- For probable-bot with reusable device cluster: refuse the account creation, but accept the email so the operator does not get a clean signal that they were detected.
- For named headless framework (Puppeteer with stealth, Playwright with stealth) or signed unverified agent: block with explicit error.
The fake-account discussion in fake account prevention has more depth on signup-specific defenses.
Login
The threat: credential stuffing and account takeover. The friction budget is lower than signup (you have an existing account relationship and can use second factors).
Working recipe:
- On every login attempt, score the device against the account’s known device history.
- For known device + clean fingerprint: allow. The user does not see anything.
- For new device + clean fingerprint + reasonable IP: allow, but require email or SMS notification.
- For new device + suspicious fingerprint, or known device + impossible travel: step-up. Push to passkey, TOTP, or push notification on a known mobile device.
- For automation signal (named framework, headless artefact, replayed canvas hash): block before the password check. Do not reveal whether the username exists.
Note the “before the password check” detail. A common mistake is putting bot detection after credential verification, which lets attackers use the response timing as an oracle for valid usernames.
Checkout
The threat: payment fraud, card testing, BIN attacks. Friction budget is moderate; an interrupted checkout is a lost sale, but undetected fraud is more expensive.
Working recipe:
- Score the session continuously through the cart and checkout flow.
- For known good device + matching billing address: allow.
- For new device or anomalous fingerprint: 3D Secure step-up (the existing card-network pattern).
- For card-testing signature (rapid attempts across many BINs from the same device, often with fingerprint variation that does not match a real session): block, log the device, and add it to a temporary deny-list at the next attempt.
- For named framework: block.
We cover this in detail in payment fraud detection.
Search and content APIs
The threat: content scraping, price scraping, competitive intelligence. Friction budget is very high for a logged-in real user, very low for a scraper because the value of a single failed request is small.
Working recipe:
- For verified search engine crawlers: allow at a generous rate.
- For verified AI agents authorized by the user: allow at a moderate rate.
- For unverified AI agents: throttle to a reasonable per-session rate that lets one-off retrieval work but makes large crawls uneconomic.
- For named scraping framework or anti-detect browser: serve cached or stale data, or insert minor inaccuracies (poison the dataset), rather than blocking outright. Blocking tells the operator they have been detected.
- For real users: allow.
See web scraping prevention and price scraping for more.
API endpoints (server-to-server)
The threat: data exfiltration, abuse of free-tier rate limits, unauthorized programmatic access. Friction budget is conditional on API key trust.
Working recipe:
- Authenticate every API request. If it has no key or token, block.
- For authenticated calls: track per-API-key behavior against the key’s known patterns.
- For anomalous behavior from a valid key (volume spike, IP change, time-of-day shift): rate-limit and flag.
- For credential abuse (one key being used from many IPs simultaneously): require key rotation.
Sealed server-side decisioning
The mitigation pattern that has become a standard since 2023 is sealed server-side decisioning. The architecture has three components.
Client SDK. A small piece of code in the user’s browser or app that collects signals and emits a session token. The token is opaque to the client.
Sealed decision service. A server that holds the scoring model and the device intelligence. The application sends the session token to this service and gets back a verdict, either inline at request time or asynchronously.
Sealed enforcement. The verdict, when the application chooses to act on it, is enforced server-side. The user-visible response is a normal 200, 401, 403 or step-up redirect.
“Sealed” means the scoring model and the device history are never exposed to the client, so the client cannot reverse-engineer the rules, replay scores, or game the verdict by sending different inputs.
This matters because open-source fingerprinting libraries that compute scores client-side are easy to evaluate but lose effectiveness within weeks of being targeted; attackers can read the rules and bypass them mechanically. Sealed scoring forces the attacker to discover the rules through black-box testing, which is much more expensive.
The Foil SDK is built on this pattern: the SDK collects, the API returns a verdict, and the application enforces. DataDome, HUMAN, and most other modern bot management vendors use the same architecture with their own implementations.
Proof-of-work is replacing CAPTCHA
The default challenge in 2023 was CAPTCHA. In 2026, proof-of-work has become the first choice for new deployments that want an invisible challenge. Plenty of CAPTCHA is still in production, but the direction of travel is clear.
A proof-of-work challenge requires the browser to compute a small cryptographic puzzle (typically a SHA-256 preimage with a target difficulty) before the protected request is allowed. The cost to a real user is a few hundred milliseconds of CPU time. The cost to a bot operator running ten thousand sessions in parallel is meaningful, because each session has to do the work independently and the work scales linearly with attempt volume (Arkose: Beyond CAPTCHA: Proof of Work).
Try sliding the difficulty up. Every additional bit roughly doubles the expected work. A real-world deployment lands around 16–20 bits: fast enough to be invisible to the visitor, expensive enough that an attacker mounting thousands of parallel attempts pays for every one of them in CPU time.
Compared to CAPTCHA, proof-of-work has three advantages:
- No accessibility tax. A CAPTCHA penalises visually impaired users, users on low-end devices, and users in languages and regions the CAPTCHA provider serves poorly. PoW demands nothing from the user except a moment of CPU time.
- Linearly economic for attackers. Solving 10,000 CAPTCHAs costs $1 to $10 at current rates. Computing 10,000 PoWs costs CPU time that the attacker has to provision.
- No user-facing UI. PoW is invisible, where a CAPTCHA by definition is not.
The disadvantages: PoW does not stop a determined attacker, just slows them down. And the CPU cost is not free on the user side either: on low-end mobile devices the puzzle eats battery and thermal headroom that desktop users never notice, so difficulty should be tuned per platform rather than set globally. It is best paired with detection: high-confidence bots get PoW plus throttling plus logging. For the full menu of challenge replacements (attestation, managed challenges, honeypots, step-up), see CAPTCHA alternatives.
Mitigation that is invisible to real users
Three principles for keeping mitigation invisible.
Score before you act. Run the detection during natural page interaction. The score is ready by the time the user submits anything important. The mitigation decision is then a server-side branch with no extra user-visible latency.
Default to less. When the score is ambiguous, default to a less-aggressive action. Throttle before challenge, challenge before block. The cost of a wrong block exceeds the cost of a wrong allow for most route types.
Use second factors for step-up, not first-time friction. A user typing their password is not the moment to introduce a CAPTCHA. A user transferring $5,000 is. Reserve high-friction actions for high-stakes events.
Mitigation that survives operator rotation
Bot operators iterate. A mitigation rule that worked last week may be defeated this week. The properties of a mitigation that holds up:
Multi-signal. The rule fires on the joint distribution of several signals, not a single one. Patching one signal does not bypass it.
Soft enforcement initially. When you add a new rule, run it in shadow (log only) for a few days. Look at what would have been blocked. Adjust before enabling enforcement.
Per-classification, not per-signature. A rule that fires on “any unverified Browser Use instance” survives Browser Use’s next release. A rule that fires on a specific User-Agent string does not.
Reversible. Every rule has an off switch, a small set of operators can disable rules in seconds, and there is a runbook for doing so.
How Foil supports mitigation
Foil emits a decision per session: a verdict (bot, human, or inconclusive), a risk score, attribution labels naming the framework or agent when one is identified, and a visitor fingerprint that persists across resets. The application code in the middle of your request handler reads this decision and applies its own policy. The two work together but they are deliberately separated.
A typical integration looks like this in an Express middleware. The browser SDK emits a sealed token, the client sends it along with the request, and the server verifies it inline:
import { safeVerifyFoilToken } from '@abxy/foil-server';
app.post('/api/login', async (req, res, next) => {
const result = safeVerifyFoilToken(req.body.foilToken, process.env.FOIL_SECRET_KEY);
if (!result.ok) return next(); // no or invalid token: apply your default policy
const { decision, visitor_fingerprint } = result.data;
if (decision.verdict === 'bot') {
return res.status(403).json({ error: 'automation_not_permitted' });
}
if (decision.verdict === 'inconclusive') {
// impose cost rather than blocking
return requireProofOfWork(req, res, next);
}
// human: check the visitor fingerprint against the account's device history
const known = await isKnownDevice(req.body.email, visitor_fingerprint?.id);
return known ? next() : require2FA(req, res); // 2FA for a first-time device
});
The detection logic lives in the decision rather than in the middleware, which encodes only the application’s policy.
For the bigger picture, bot detection covers what the detection layer produces, and bot management covers the policy decisions the application makes with it.
Further reading
- Arkose, Beyond CAPTCHA: Proof of Work: arkoselabs.com/blog/proof-of-work
- Cloudflare, Challenge bad bots (concepts): developers.cloudflare.com/waf/custom-rules
- Fastly, Bot Management documentation: docs.fastly.com/products/bot-management
- AWS, WAF Bot Control rule group: docs.aws.amazon.com/waf
- ActiveProspect, Bot mitigation news and trends in 2026: activeprospect.com/blog