Technical Brief

Bot Detection Logic — Specification

The full specification of the AdFraud Shield detection engine: 11 signals are implemented, and the 8 active in the current configuration are disclosed structurally across 5 layers — from protocol-level to behavioral analysis. (The other 3 are network-edge signals, not enabled in the current configuration; see below.)

Last updated: 2026-08-26 (false-positive figure is an estimate; measured values will be published once calculated)

1. Design philosophy

Most legacy bot defenses lean on User-Agent strings and IP blacklists. Both are easy to spoof — a residential proxy and a tweaked UA glide right through.

AdFraud Shield prioritizes signals that are technically expensive to forge — like a randomized Canvas/WebGL renderer that exposes headless Chrome. (The engine also implements edge-derived signals such as TLS JA4 and TCP TTL; those require a network-edge layer and are not enabled in the current configuration.)

This brief explains why each signal is hard to spoof. Weights and thresholds are not disclosed (competitive defense), but the structure and reasoning are.

2. 5 layers · 8 active signals (11 implemented)

Inbound clicks travel through five filters. Signals within a layer are evaluated independently and combined with weights at the scoring stage.

Full transparency: 8 of the 11 signals implemented in the engine are active in the current configuration. The 3 marked "Edge-dependent" (TTL, TLS JA4, HTTP/2) derive their data from a network-edge layer and are not enabled in the current configuration. We disclose them in full rather than hide them.

Layer 1: Network

The hardest layer to spoof. The OS kernel itself betrays the impostor.

UA/OS contradiction

Cross-check the OS claimed in the User-Agent against the OS implied by the TCP TTL. Windows UA + TTL=64 (Linux family) is a strong contradiction.

TTL hop analysis

Edge-dependent

Initial TTL by OS (Windows=128, Unix=64, Cisco=255) reveals the hop count. ≥19 hops strongly implies a proxy.

Layer 2: Protocol

TLS and HTTP/2 handshakes have implementation-specific byte ordering — a powerful fingerprint.

TLS JA4 fingerprint

Edge-dependent

Hash the ClientHello cipher suites, extensions, and elliptic curves into a JA4 hash. Compare against known patterns from Puppeteer, Selenium, curl, Go, Node.js, etc.

HTTP/2 SETTINGS frame

Edge-dependent

HTTP/2 SETTINGS parameter ordering and WINDOW_UPDATE timing are implementation-specific. Go net/http and Python httpx send a different order than Chrome and reveal themselves immediately.

Layer 3: Browser

Client-environment fingerprints captured via JavaScript.

Client fingerprint analysis

navigator.webdriver === true is a confirmed bot signal. Canvas/WebGL renderer strings matching SwiftShader, Mesa OffScreen, or llvmpipe (headless GPU implementations) confirm a bot. We also cross-check User-Agent OS against navigator.platform OS.

Layer 4: Identity

Who is this IP, and is the geography consistent?

Datacenter IP detection

Match against ~140 known cloud CIDR ranges (AWS, GCP, Azure, DigitalOcean, Linode, Vultr, OVH, Hetzner, etc.). Binary-searched O(log n), no external API dependency.

Geo/timezone reconciliation

Compare the browser's Intl.DateTimeFormat timezone to the IP geolocation country. The moment a datacenter IP claims Tokyo, we have a contradiction. Language-compatibility check tempers travel/VPN false positives.

Layer 5: Behavior

How the visitor behaves on the page. Bots usually skip the human theatrics.

Frequency / repeat-click detection

Counter on the (IP + fingerprint) composite key, 5-minute window in Redis. Composite keying avoids the CGNAT/corporate NAT false-positive trap that pure-IP counting falls into.

Behavioral honeypot

Invisible DOM links and form fields that no real user reaches. Touching one is among the highest-confidence bot signals we have. Scrapers fall in by structure.

User behavior analysis

Dwell time, scroll depth, touch/mouse interactions sent on visibilitychange. Instant exit (<1s) or full-scroll-in-2s patterns are flagged.

Ad-click fraud (click farm)

Visits that carry an ad click-ID (gclid / ttclid / fbclid / yclid) yet bounce within roughly a second with zero interaction — the real-device click-farm and cookie-warming signature. In-app WebViews get a relaxed threshold so genuine readers aren't swept up.

3. Dynamic scoring & cross-layer convergence

Each signal returns a 0-100 score; the risk-scoring module combines them with weights. Weights and per-signal score tables are withheld for competitive defense, but the verdict flow is disclosed.

Verdict by total score:

  • 0-30: human (no exclusion)
  • 31-70: suspicious (exclusion candidate)
  • 71-100: bot (eligible for protection actions. Google Ads exclusion and pixel firing additionally require a score of 85+ — configurable per site; IP exclusion is IPv4-only)

Cross-Layer Convergence

Convergence is the default: when two or more layers (e.g., Network + Protocol) independently flag contradictions, a boost raises confidence. This is the structural defense against "forge one signal and slip through." Deterministic single signals — navigator.webdriver being true, for example — get a dedicated boost instead, so a decisive finding is not diluted by averaging.

4. Human-First Safeguards

Zero false positives is impossible for any bot detector. AdFraud Shield ships with active, in-system mechanisms that detect and reverse mistakenly-excluded IPs.

24-hour cooldown

Any IP added to the Google Ads exclusion list is automatically re-evaluated 24 hours later. Designed to handle dynamic IP reassignment (carrier NAT etc.).

7-day human/bot ratio guard

An IP whose past-7-day human verdicts outnumber its bot verdicts is automatically removed from exclusion candidates — even on a brief bot-score spike. Triggers at 5+ samples.

Methodology

FP rate = (excluded IPs whose subsequent 7-day human verdicts > bot verdicts) ÷ (total excluded IPs).

Operating under the definition above, we expect a false-positive rate below 0.1%. This is an estimate, not a guarantee, and individual sites will vary. Measured values will be published together with their measurement window once available.

Case: pilot customer FP guard auto-correction (2026-04-28)

On a large e-commerce pilot customer, the Google Ads top-500 exclusion list reached 11% (55 IPs) of human-dominant contamination during early operation. The 7-day ratio guard identified all contaminated IPs within 24 hours and auto-corrected the list to 0%. A failure mode that would be invisible to manual operation was caught by the system itself.

5. Compliance & Performance

APPI compliance (Japan's Personal Information Protection Act)

We do not collect direct identifiers of end users (no name, email, or phone). Technical identifiers — IP, User-Agent, and device fingerprint — are collected on behalf of the site operator (the data controller) and auto-deleted after 7 days. The only exception: evidence records of ad clicks classified as invalid (click IDs, anonymized network prefixes — never the full IP) are kept up to 180 days solely for platform fraud reporting.

No third-party cookies

Bot detection sets no cookies at all and never depends on them. Optional conversion tracking, only if you enable it, adds a single first-party cookie — never third-party — so Apple ITP and Google Privacy Sandbox don't affect you.

Performance

JS tag (v0.4.2, measured): 18,703 B minified (~18.3 KiB), ~7.0 KB gzipped as served by the CDN, async load. LCP / FCP / TTI deltas stay within measurement noise. Lighthouse scores effectively unchanged.

Data retention

Raw data (visits, verdicts): 7 days. Aggregates (daily_site_stats, daily_signal_stats): permanent. Excluded IP list: 30-day TTL from last detection.