Half your traffic is not a person.
Loghound proves which half.
An open-source traffic analyser and bot detector for the access logs you already have. It catches headless Chrome on rotating residential proxies — the traffic every analytics product on the market counts as human — and it measures how long somebody actually looked at your page, not how long a tab existed.
Free and open source, MIT licensed. No account, no cloud, no telemetry. It runs on your server, reads your logs read-only, and stores everything in a Solr you own — or one we host for you.
It detects the bots everything else counts as human
Not a User-Agent blocklist. Dozens of addresses on unrelated consumer ISPs, sharing one byte-identical header fingerprint, each fetching a single page. From inside any one plane of observation that is indistinguishable from a crowd of real visitors. Loghound watches three planes and cross-checks them.
It measures real time on site
Conventional analytics pings while the page is merely loaded, so a tab left open in a background window reports as an hour of engagement. Loghound keeps three separate clocks — wall, visible, engaged — and never conflates them.
The traffic looks human from every angle you are currently looking from
Loghound was not written from a threat model. It was written because a capture of this site's own traffic did not survive being looked at properly.
Roughly half of the sessions a conventional analytics tool reported on opensolr.com carried a hard bot signature. Identical header fingerprints across unrelated consumer ISPs. One page each, never two. Every one of them reported at exactly ten seconds on site.
They were not caught by anything, and it is worth being precise about why. This traffic does everything right:
What it does that defeats the usual checks
Every one of these is why the ordinary defences report nothing.
- It fetches sub-resources. Images, CSS, JavaScript, the favicon. "No assets" never fires.
- It executes JavaScript. It reaches the site's own analytics endpoint with a full payload — screen resolution, language, timezone. That endpoint is only ever reached by code that ran in a JavaScript engine, so the site's analytics counted every one of them as a human visitor.
- The User-Agents are ordinary. Recent Chrome on Windows 10 and on Linux. Nothing in any blocklist.
- The addresses are spread across unrelated networks, which defeats every per-address rate limit.
- One of them even downloads a 10 MB file — engagement, by any conventional measure.
What it cannot hide
The whole value of a rotating proxy fleet is that it is one automation stack wearing many addresses.
- The header tuple is byte-identical across addresses on completely different ISPs. It has to be: the stack behind them is one program.
- Three of them began a session in the same second. Then two more did it again seven minutes later. Five independent humans do not do that.
- Every one fetched exactly one page and left. The session shape is identical across all of them.
- Nobody scrolled, clicked or moved a pointer. Ten seconds of "time on site" with no interaction of any kind in it.
Hash everything except the address
A fleet on rotating residential proxies changes its exit address on every request and changes nothing else. So any fingerprint that includes the address identifies nothing, and one that deliberately excludes it collapses the fleet back into a single row. The rotation stops being camouflage and becomes the signature.
The fingerprint-cluster view, in outline: one row per header fingerprint, with the count of distinct addresses that shared it, expanding to the member addresses and their networks. Five or more on non-mobile networks within a day is worth 80 of the 100 points needed for a bot verdict — enough on its own. This is a diagram of the view, not a screenshot.
Three independent planes, cross-checked
Each plane alone is defeatable, and essentially every existing tool uses exactly one of them. All the power is in correlating the three — because the things that make a scraper survive the JavaScript plane are the things that give it away on the behavioural one.
Log-only analysers see plane 1. JavaScript-only analytics sees plane 3, and trusts it: a client that runs the script is a human, as far as it is concerned. That single assumption is why headless Chrome on residential proxies is counted as human traffic almost everywhere.
It reads the logs you already have
Apache, nginx and Caddy, including JSON formats. The setup wizard reads your web server configuration for the log format and file list, then shows you the mapping with five of your own log lines rendered as parsed records and a confidence percentage. Nothing is ever ingested with a silently guessed format.
It stores into Solr
One document per log line, one per session. The fingerprint-cluster analysis is a JSON Facet query, which is why it stays fast on a busy site instead of turning into a table scan. Run your own Solr, or let Opensolr host the index.
An optional JavaScript beacon
One line on your site. It reports whether JavaScript ran at all, whether the engine actually matches the version the User-Agent claims — a spoofed User-Agent cannot retrofit a JavaScript engine — and whether a human plausibly drove the pointer. No cookies, nothing stored on the visitor's device.
Every verdict carries its reasons
A score with no explanation is not evidence, so a verdict without reasons is treated as a bug rather than a rounding error. Seventeen weighted rules, each with a code, and the ruleset version is written onto every session so a historical verdict stays traceable to the rules that produced it.
Honest crawlers are kept separate
Googlebot and GPTBot are bots, and counting them as human traffic is how dashboards start lying to you — but they are presented apart from evasive traffic, always. Conflating "Googlebot indexed 400 pages" with "someone is scraping you from 200 residential addresses" is exactly what makes the usual tools useless here.
Your log files are never touched
Opened read-only, and never written to, truncated, rotated, renamed or deleted. The tailer survives logrotate, copytruncate, deletion while the file is held open, and inode recycling — the case where a rotation hands the new file the number the old one just freed and a naive tailer quietly stops ingesting while looking perfectly healthy.
Four numbers, kept apart
A visitor opens your article in a background tab at nine, gets distracted, comes back twenty minutes later, reads for four minutes with regular scrolling, then switches window and leaves the tab open until ten. Here is what each tool would tell you about that visit.
Bars to scale. The industry number is fifteen times the true one on this visit, and that is not an edge case — it is what a browser with tabs does all day.
How the clocks work
Three of them come from the beacon; the fourth comes from the log alone.
- Wall runs while the page exists. Nothing else.
- Visible runs only while the tab is on screen and the window has focus.
- Engaged runs only while visible and within thirty seconds of a real scroll, click, keypress or pointer movement.
- Log span is last request minus first request — the only number a log-only tool has, and it is structurally blind to the last page of every visit.
Why the numbers are trustworthy
Small decisions, but they are the difference between a measurement and a guess.
- It sees the last page of a session. The beacon flushes on pagehide, so the final pageview is measured — something a log-based tool structurally cannot do, because there is no later request to measure against.
- Monotonic clocks only. A clock correction or a daylight-saving change can never be added to somebody's reading time.
- A page that starts hidden accrues nothing. Prerendered and background-tab pages start at zero and stay there until they are actually shown.
- Missing stays missing. When no beacon arrived, the timing fields are absent rather than zero — a zero would enter every average as a real "0 seconds engaged" observation and quietly destroy the metric.
Written to be fair
Each of these is good at what it set out to do, and for most people one of them is the right answer. Loghound is narrow on purpose, and the last row of this table is the one worth reading.
| Loghound | GoAccess | Matomo | Plausible | GA4 | |
|---|---|---|---|---|---|
| Data source | logs and a JS beacon | logs only | JavaScript, with a log importer | JavaScript | JavaScript |
| Bot detection | three-plane correlation, 17 weighted rules, every verdict carries its reasons | User-Agent blocklist | User-Agent and address lists, plus some heuristics | User-Agent blocklist | undisclosed, not inspectable |
| Catches headless Chrome on residential proxies | yes — that is the point | no | no | no | no |
| Cross-address fingerprint clustering | yes — the primary signal | no | no | no | no |
| Time on site | four distinct numbers, including engaged time and the last page | request span only | tab-open time | tab-open time | tab-open time |
| Storage | Solr 9, yours or hosted | in-memory or an on-disk report | MySQL or MariaDB | ClickHouse | |
| Install | clone and run one script; no Composer, no npm | one binary from your package manager | a PHP application plus a database, or their cloud | Docker and Postgres, or their cloud | nothing to install |
| Cost | free, MIT | free, MIT | free self-hosted, paid cloud | paid cloud, free self-hosted | free |
| Where it is genuinely better than Loghound | — | Faster, zero moving parts, a beautiful terminal interface, and it will parse a huge log file in seconds with nothing installed. If your question is "what happened in this log file, now", use GoAccess. | A complete analytics product: goals, funnels, ecommerce, heatmaps, A/B testing, session recording, a large plugin ecosystem, mature privacy tooling and a real company behind it. | The nicest interface in analytics, a tiny script, no cookies at all, genuinely simple. Excellent for a content site that wants honest pageviews without a project. | Free at any scale, integrates with the rest of Google's stack, and the attribution modelling is real work nobody else has replicated. |
A bot detector that oversells itself is worse than none
Because you would stop looking. So here is the honest list, and it is the same list that opens the project's own README.
A well-funded, careful scraper
Give every session a genuinely unique and mutually consistent header fingerprint, run a real non-headless browser, synthesise human-like pointer movement and pacing, never reuse an exit address — and Loghound will classify it as human. Everything here raises the cost of scraping. None of it makes scraping impossible, and anyone claiming otherwise is selling something.
Privacy-conscious humans get flagged
The single biggest source of false positives, and it deserves to be first. A visitor running a script blocker, strict tracking protection or a locked-down browser mode may never load the beacon, and from the log alone that is indistinguishable from a non-JavaScript client. The rule for it is deliberately weighted below the threshold so it can never condemn anyone on its own.
Traffic that never reaches your origin
A request served from a CDN edge or a cache in front of you produces no origin log line and does not exist as far as Loghound is concerned. If you run a CDN, you are analysing your cache misses — and Loghound will not silently pretend otherwise.
Low-and-slow scrapers
One page, one address, one fingerprint, once a day, from a residential network. It sits below every threshold in the ruleset by construction. The cluster signal — the strongest thing here — needs a fleet to detect a fleet.
API and native-app traffic
There is no beacon in a native application, so the execution plane is permanently blind there and every client looks like a non-JavaScript one. Point Loghound at that vhost's log and read the transport plane instead.
Carrier-grade NAT
Thousands of real mobile users share a small pool of addresses, which is why the fleet rule explicitly excludes mobile networks — and why an ASN misclassification turns into a batch of false positives rather than one.
It is not a firewall and it blocks nothing
Loghound observes and reports. It issues no challenges, rate-limits nothing and writes no firewall rules. Deciding what to do about what it finds is your job, and that is precisely why every verdict ships with its reasons attached.
It does not do product analytics
No funnels, no goals, no A/B tests, no revenue attribution, no cohort retention. If you need those, one of the tools in the table above is the right answer — and Loghound runs alongside it.
Clone it, and look before you leap
PHP 8.1 or newer with five standard extensions, Solr 9, systemd, and read access to your access logs. That is the whole dependency list — there is no Composer, no npm and no build step, on purpose, so git clone plus one script works on a bare box.
It will not break your box
It never modifies an existing vhost, PHP-FPM pool, cron entry or service, never overwrites a file it did not write, validates every configuration it generates before enabling it, rolls back if validation fails, and reloads rather than restarts so other sites are undisturbed. It also refuses to deploy a TLS certificate that does not chain to a trusted root.
It works on plain combined
Zero configuration on a default Apache or nginx. Logging a handful of extra headers makes the fingerprint substantially sharper — the documentation gives you the block to paste and a table saying exactly which detection signals each individual header buys, so you can take some of it and not the rest.
Bring your own Solr, or use ours
Point it at any Solr 9 you run and it pushes the two configsets itself. If you would rather not operate one, Opensolr can host the index — setup asks for an email address and an API key, creates both indexes, pushes the configsets and verifies them, and you never have to learn what a configset is.
Find out what your traffic actually is
Loghound is free, MIT licensed and self-hosted. There is no account to create, nothing phones home, and the whole thing is one git clone away. If you would rather not run a Solr for it, we will host the index.