Protection overview
How every click is scored after the redirect — signals, weights, verdict thresholds, the auto-blocklist and incidents.
Updated 2026-09-02
Protection runs after the 302 is sent, so it never adds latency. Each click gets a riskScore (0–100), a list of reasons and a verdict.
Signals#
| Group | Signals |
|---|---|
| IP reputation | Datacenter / hosting range, VPN, proxy, Tor exit, CDN, ASN risk weight — from the IP pool and live intel |
| Automation | Headless / webdriver UA, missing or inconsistent client hints, known bot signatures (non-verified crawlers) |
| Consistency | ValueTrack device vs. real UA, loc_physical_ms vs. IP geo, language vs. country, screen / timezone from the beacon |
| Frequency | Clicks per IP (/64 for IPv6) per link and per campaign inside sliding windows; burst detection |
| gclid replay | Same gclid seen more than once, or a gclid older than Google's validity |
| Engagement | Beacon missing after N seconds, zero dwell / scroll, fingerprint reuse across many IPs |
| Allowlists | Verified Googlebot / Bingbot ranges and your tenant allowlist are never flagged |
Weights are configurable under Protect → Rules; sensible defaults ship out of the box.
Verdicts#
| Verdict | Score | Effect |
|---|---|---|
VALID | below the suspicious threshold (default 40) | Counted normally |
SUSPICIOUS | 40–69 | Reported; excluded from ROAS "valid" totals; no action |
INVALID | ≥ 70 (block threshold) | IP added to the blocklist, click excluded, Google Ads negative IP on next sync |
Thresholds are per organization. You can override a verdict manually (Clicks → Mark valid / invalid); overrides are audited and feed back into the weights report.
Auto-blocklist#
Invalid clicks add their IP (or the /64 for IPv6) to IpBlock with the reasons and score. Entries expire after the configured TTL (default 30 days, refreshed on new hits). Manual and imported entries never expire unless you set a date. The blocklist applies to:
- routing (
blockforipType-based rules orblockBots), - Google Ads negative IP criteria (sync),
- the edge worker (KV-cached, so blocked IPs are stopped before the origin).
Incidents#
A burst of invalid clicks on one campaign (default: > 25 invalid in 10 minutes or invalid rate > 40 % with ≥ 50 clicks) opens an incident: a webhook is sent, the campaign can be auto-paused through the Google Ads API, and the incident stays open until resolved. See Protect → Incidents.
Install fraud#
Installs get their own flags (CTIT anomalies, click flooding, device farms, SDK spoofing, duplicate devices) — documented under Attribution methods.
Evidence#
GET /api/protect/export?days=30 produces the CSV Google asks for in an invalid-activity investigation: timestamp, IP, gclid, campaign, reasons, score. The MCP tool get_protection_stats returns the same aggregates.
Double-click, repeat and flood defence#
Not every invalid click comes from a datacenter. WhichClick links clicks across time, subnets and fingerprints so repeat abuse from ordinary-looking addresses is still caught.
| Signal | What it means | Weight | Blocks IP? |
|---|---|---|---|
double_click | Same IP hit the same link again inside the duplicate window (default 3 s). The duplicate is marked invalid, gets no estimated cost and is excluded from frequency counts. | 70 | No — a human double-click must never ban a customer. |
rapid_repeat | 3+ clicks from one IP inside the rapid window (default 60 s). | 35–65 | Yes, when the total score crosses the block threshold. |
metronomic_clicks | 5+ clicks in an hour with near-constant gaps (coefficient of variation < 0.15) — a scripted timer. | 50 | Yes |
subnet_frequency | 10+ clicks from 3+ distinct IPs in the same /24 (IPv4) or /64 (IPv6) within an hour. | 35–60 | Yes |
fingerprint_rotation | One IP presenting 5+ different request fingerprints in an hour (spoofed user-agents). | 45 | Yes |
fingerprint_reuse_ips | One request fingerprint arriving from 5+ IPs in a day (one bot behind rotating proxies). | 50 | Yes |
campaign_velocity_spike | Campaign clicks/min ≥ multiplier × its 24h baseline (min 10/min). Applied to every click in the spike so clean-looking flood traffic becomes at least suspicious. | 30–55 | Yes, in combination |
The request fingerprint is a salted hash of the user-agent, Accept* headers and Client Hints (Sec-CH-UA*). The subnet key is hashed per day, like the IP hash, so neither is reversible.
Incidents#
Two incident kinds are opened automatically and shown under Protect → Incidents:
- Invalid-click attack — more than the configured number of invalid clicks in 10 minutes. Mitigations: IP auto-block, Google Ads IP exclusion sync, optional campaign pause.
- Click flood — total clicks in 10 minutes ≥ multiplier × the campaign's 24h baseline, regardless of individual verdicts. Every click in the window carries the velocity penalty; optional campaign pause applies.
Both post to the alert webhook. Slack webhooks (hooks.slack.com) receive a formatted Block Kit message with a link to the incident list; other endpoints receive JSON (type: click_attack | click_flood). Incidents auto-resolve after 30 quiet minutes.
Tuning#
Protect → Rules exposes the duplicate and rapid-repeat windows, subnet aggregation, flood detection and the spike multiplier. Lower the duplicate window for high-intent landing pages where legitimate repeat visits are common; raise the spike multiplier for campaigns with bursty, scheduled traffic (TV spots, push sends).