The scoring rules

Weighted additive, nought to a hundred, and eighty or more means the signal alone is a verdict.

Weighted, additive, nought to a hundred, capped at a hundred. Every point added carries a reason code, and a verdict with no reasons is a bug rather than a terse answer.

Why the numbers are what they are

The weights follow one principle: eighty or more is a claim that this signal alone is enough to call something a bot, because eighty is the bot threshold. So the question for every rule is not “how suspicious is this” but “am I willing to publish a verdict on this evidence and nothing else”. Seven of the seventeen clear that bar. Everything below eighty is designed to stack — three thirty-point behavioural rules reaching ninety is the intended path for a scraper that leaves no single decisive mark.

The panel shows human labels rather than these identifiers. Identifiers and labels is the mapping, and it is the page to read if you are writing your own query or reading a filter URL.

01 · Decisive alone — eighty or more
WeightLabelWhat it actually tests
100Automation markerA definitive driver artefact was exposed to the page: the webdriver flag, a chromedriver global, or a Puppeteer, Playwright or Selenium hook. Browsers do not have these; drivers do. There is no false positive here — a page cannot acquire them by accident.
100Declared crawlerIt said it was a bot and it was telling the truth. Verdict bot, threat none. See section 04.
95rDNS claim failedIt declared itself a major search crawler and forward-confirmed reverse DNS did not back that up. This is impersonation, not automation, and forward-confirmed reverse DNS is the mechanism the search engines themselves document for verifying their crawlers.
90Headless rendererWebGL reported a software rasteriser — SwiftShader, llvmpipe, Mesa OffScreen or Microsoft Basic Render — which is what you get when there is no screen. Not 100, because a virtual machine, a remote desktop session or broken graphics drivers can legitimately land here.
90Forged beacon timingThe claimed dwell time is impossible against the issue time of its own token, by more than a five-second tolerance. Within that tolerance the number is clamped silently and nothing fires, because one-second token resolution plus a heartbeat queued behind a busy main thread produces honest overshoot. The lie is recorded rather than discarded, because the lie is the evidence.
85UA claim failedThe engine lacks a feature that shipped in the browser version the User-Agent claims, or has one that shipped after it — with a grace of two major versions in each direction, and iOS browsers skipped outright because they are all one engine wearing another’s User-Agent. A spoofed User-Agent cannot retrofit a JavaScript engine.
80Proxy fleet fingerprintFive or more distinct addresses shared this exact header fingerprint within twenty-four hours on non-mobile networks, and it is not a crawler whose reverse DNS check passed. One client, many exits.
02 · Designed to stack — below eighty
WeightLabelWhat it actually tests
75Sec-CH-UA mismatchA Chrome User-Agent arrived without the matching client hint, or with one that contradicts it. Chrome always sends its own client hints on a secure origin. Requires the header to be logged.
70Platform mismatchThe platform client hint disagrees with the operating system the User-Agent claims. Windows in the User-Agent and Linux in the hint is a headless container in a costume.
70No JS on HTMLAn HTML page was served with a 200, the User-Agent claimed a real browser, and no beacon ever arrived — and the beacon is known to be deployed on this site. That gate is not optional: on a site that never installed the snippet, every session looks like this and the rule would condemn the whole audience. Weighted 70 for the same reason in miniature, so it cannot condemn anybody on its own.
45Datacentre + browser UAA consumer browser User-Agent arriving from hosting address space. People do browse from servers, so this is not decisive — but it is unusual enough to be worth stacking.
45Periodic timingFour or more gaps between requests, a median gap of at least a second, and a spread below ten per cent of that median. Relative rather than absolute, because a 200 ms rhythm and a 30 s rhythm are equally mechanical; the median floor keeps a burst of asset fetches from looking like a metronome. People are not metronomes.
40No interactionThe beacon ran, the session ended, and not one scroll, click, key press or touch ever happened. Possible for a human reading a page on a static screen, which is why it is 40 and not 80.
35Timezone mismatchThe browser timezone disagrees with the timezone derived from the address. Travellers, VPN users and anyone with a deliberately set timezone trip this legitimately, so it is low. It is a stacking signal: meaningless alone, meaningful next to a fingerprint cluster.
30No conditional requestsAt least two assets were re-requested with no conditional header ever sent, and this site is known to answer with a not-modified status at all. Both extra conditions exist to avoid blaming the client for the server’s behaviour.
25No sub-resourcesHTML was fetched and not a single stylesheet, script, font or image followed it. The classic scripted-client signature — only 25, because a warm cache, a text-only browser or a prefetch can all look like this.
15Single page, under 10sOne page, gone in ten seconds or less. Weak alone and it is supposed to be weak — plenty of humans bounce. It exists to push a session that has already accumulated other signals over a threshold.
03 · Thresholds, and the ladder
BOT SCOREHUMANLIKELY HUMANUNKNOWNLIKELY BOTBOT020406080100Seven rules land at or above 80 on their own. Everything below it has to stack:no sub-resources 25 + no conditional requests 30 + timezone mismatch 35 = 90, a bot on three weak signals.

Figure 1 — the verdict bands. Unknown is a real answer and it is used. A session with 45 points is not “probably a bot”; it is a session Loghound does not have enough evidence about, and saying so is more useful than guessing.

A session also carries a class saying what kind of automation it was — headless browser, scripted client, declared crawler, AI crawler, monitor, spoofed User-Agent, proxy fleet, or not automation at all. The vocabulary page lists them with what each one means.

04 · Honest crawlers are not the enemy

Major search crawlers and AI crawlers identify themselves. They get the verdict bot — they are bots, and counting them as human traffic is how analytics dashboards end up lying to you — with a class that says which kind.

The panel presents them separately from evasive traffic, always

Conflating “a search engine indexed four hundred pages” with “someone is scraping you from two hundred residential addresses” is precisely what makes existing tools useless for this. They are opposite facts: one is a service you want, the other is a cost you are paying. The separation is enforced in the ruleset rather than by a checkbox in the interface, because every consumer of a session document must see the same split.

An AI-crawler flag is set for around thirty named agents, so “how much of my content is being taken for model training” is one facet away. Note that several of those are user-triggered fetchers rather than training crawlers — they fetch a page because a person asked an assistant about it. They are grouped together because operators consistently want them in one bucket, but the two are not the same event.

05 · Scoring a session that has not ended yet

A session document is written for an open session too, on every scorer run, so the panel shows traffic within a minute of ingestion starting rather than after the thirty-minute idle timeout. Such a document is marked provisional, and it is scored differently.

Five of the seventeen rules fire on the absence of something a session may still go on to do, and every one of them would accuse a live human visitor:

Deferred ruleWhat its absence means while the session is open
No JS on HTMLThe beacon reports on page hide — a visitor still reading has not sent one
No sub-resourcesThe sub-resource log lines have not been written yet
No conditional requestsNothing has been re-fetched yet, and no conditional response has arrived yet
No interactionThe visitor has not scrolled or clicked yet
Single page, under 10sEvery session is one page and under ten seconds at second one

So a provisional verdict rests only on evidence already present, and it is floored at unknown: it may reach likely bot or bot on positive evidence — all seven decisive-alone rules are presence-based, so an automation marker is still called immediately — but it may never claim human or likely human, because “nothing incriminating yet” on one request is not an acquittal. When the floor applies, the reasons carry a code saying so, exactly as a clean session carries one saying nothing fired.

The same discipline applies to a beacon-only session, where five transport-reading rules are silenced instead.

Next: how to change a weight, and who this ruleset gets wrong.

Loghound is open source and MIT licensed. Questions about the Opensolr half — the account, the indexes, the plan — go to opensolr.com/contact; questions about the software itself belong on GitHub.

Loghound Documentation