Click fraud is the part of your paid spend that buys a click no customer made. It has three quite different populations behind it, and the reason most write-ups on the subject are useless is that they treat it as one problem with one fix.
It is also worth being precise about the money. The ad platforms do filter invalid traffic and do issue credits. What they do not do is tell you the ruleset, and the credit arrives after the budget was already spent and the campaign already optimised against the polluted numbers. The second cost is usually the bigger one: a bidding algorithm learning from clicks that could never convert.
Three populations, three different answers
- Datacenter automation. Scrapers, rank checkers, monitoring, and headless browsers that follow every link on a SERP. Not malicious in intent, and the cheapest thing on this list to identify:
network.datacenteris true and the reason codes say so. - Residential-proxy click farms. Paid-to-click schemes and competitor-funded depletion, routed through residential pools so the exit looks like a consumer ISP. Real browsers, sometimes real hands. This is the population that makes IP blocklists pointless.
- Manual clicking by a competitor. A person, on their own device, on their own connection, clicking your ad every morning. No network signal and no device signal will tell you this is fraud, because on every axis it is a real visit. Say so plainly rather than selling a detector for it.
The first two are a detection problem. The third is a frequency problem in your own logs, and the only evidence that ever moves an ad platform on it is a visitor_id that appears on twenty clicks and zero conversions.
Measure at the landing page, not at the click
You have no access to the click; it happens inside the ad platform. You have complete access to the landing page. One evaluate call there, keyed to the click id the platform passes you, gives you a verdict you own and can query, on the visit rather than on the click.
<!-- landing page, in <head> -->
<script async src="https://maskbreak.com/assets/edge.js"
id="_mcl"></script>
<script>
addEventListener('load', async () => {
const { token, fingerprintEventId } = await Sentinel.collect();
const q = new URLSearchParams(location.search);
const clickId = q.get('gclid') || q.get('msclkid')
|| q.get('fbclid');
if (!clickId) return; // organic, or a direct visit
navigator.sendBeacon('/api/ad-visit', JSON.stringify({
token, fingerprintEventId, clickId,
campaign: q.get('utm_campaign'),
source: q.get('utm_source'),
}));
});
</script>
The server side answers immediately and scores afterwards. Nothing about this call is allowed to be on the page’s critical path — you already paid for the click, so a slow verdict must never cost you the visit as well.
// routes/ad-visit.js
app.post('/api/ad-visit', express.json(), async (req, res) => {
res.status(204).end(); // answer first, score after
const { token, fingerprintEventId, clickId, campaign, source }
= req.body || {};
if (!token || !clickId) return;
try {
const v = await sentinel.evaluate({ token, fingerprintEventId });
await db.adVisits.insert({
click_id: clickId, campaign, source,
decision: v.decision,
risk_score: v.risk_score,
visitor_id: v.device?.visitor_id,
datacenter: v.network?.datacenter === true,
reasons: v.reasons,
seen_at: new Date(),
});
} catch (err) {
log.error({ err }, 'ad visit unscored'); // fail open
}
});
What to do with the rows
Do not block the landing page. The click is already paid for. Blocking converts a wasted click into a wasted click plus a lost customer every time the verdict is wrong, and it can put a broken page in front of the platform’s own quality crawler. Filtering belongs in reporting and in bidding, not in the HTTP response.
- Weekly, group by placement. Share of visits where
decisionis notallow, by campaign, placement and keyword. A placement sitting at 40% against an account average of 6% is the finding; the absolute number on its own is not. - Feed it back as value, where the platform supports value-based bidding. Reporting a visit that could never convert as zero value teaches the bidder faster than any exclusion list, and it does not need the platform to agree with your definition of invalid.
- Use IP exclusion lists last. They are capped, they are manual, and residential pools rotate faster than you can maintain them. Worth it for a handful of persistent datacenter ranges and nothing more.
- Build the dispute from your own data. Same
visitor_id, N clicks, zero conversions, over a stated date range, with reason codes attached. That is a claim; “our traffic looks fake” is not.
Set the baseline before you judge anything
Some share of invalid traffic is normal and no campaign runs at zero. Published industry percentages are close to useless for your account because the mix depends on your product, your geography and which networks your platform is buying on that week. Run the same evaluate call on organic landing pages for a fortnight and use that as the control. Paid against organic, measured the same way, is a comparison you can defend.
What will make the numbers lie
- Counting VPN as invalid. A large and growing share of ordinary buyers browse through a consumer VPN. A VPN exit is not automation, and a policy that treats it as fraud will quietly declare your privacy-conscious customers invalid. Judge on
decisionand on the automation and datacenter reason codes, not onnetwork.vpnalone. - Screening every page view. An API call per asset and per navigation buys noise and a slower site. Score ad landings, and organic landings for the baseline. Nothing else.
- Comparing across measurement layers. If your CDN filters bots before your analytics tag runs, your analytics population and your verdict population are different sets, and the ratio between them is meaningless.
- Judging a campaign on a week of data. Click-farm activity is bursty. A placement needs a few hundred scored visits before its share means anything.
Checklist
- Evaluate on ad landings and on organic landings, nothing else.
- Beacon fires after load; the response never waits on the verdict.
- Click id, campaign, placement and
reasonsall stored on the row. - No blocking on the landing page, ever.
- Organic baseline established before any campaign is judged.
- VPN excluded from the invalid definition.
- Exclusion lists treated as a last resort, not the plan.
Frequently Asked Questions
Is click fraud the same thing as invalid traffic?
Invalid traffic is the broader term and the more useful one. It covers automation with no intent to defraud — scrapers, rank checkers, monitoring — as well as deliberate click farms and competitor clicking. Most of what shows up in a paid campaign is the first kind, which matters because the response is filtering and bid correction rather than a dispute.
Should I block visitors who score badly on the landing page?
No. You have already paid for that click, so blocking turns a wasted click into a wasted click plus a lost customer whenever the verdict is wrong, and it risks serving a broken page to the ad platform’s own crawler. Score the visit, store it, and act on it in reporting and bidding.
Can I get the money back from the ad platform?
Sometimes, partly, and late. The platforms filter invalid traffic and issue credits on their own analysis, which you do not get to see. An independent record helps a specific dispute — same visitor_id, N clicks, zero conversions, over a stated range — but treat recovered spend as a bonus and bid correction as the actual return.
Does a visitor on a VPN count as click fraud?
No. Consumer VPN use is ordinary and growing, and a VPN exit says nothing about whether a human is on the other end. Treating it as invalid traffic will mostly discard real buyers. Datacenter exits, automation reason codes and a tampering score above 0.6 are the signals that separate a machine from a person.
Score the visit, not the click
Maskbreak returns a decision, a device history and the reasons behind both in a single call. Free tier: 1,000 requests per hour, no credit card.
Try Maskbreak free →