A server log counts requests. One person reading three pages on a site with twelve assets each generates around forty lines. An analytics tag counts something different again, and neither number is wrong, they are simply measuring different things.
Every figure we publish is sessions unless it explicitly says requests. Turning one into the other requires a rule, and a rule that is not published cannot be checked, which makes any comparison built on it unfalsifiable. So ours is below.
| Rule | What it means |
|---|---|
| Identity | A visitor is hash(address + user agent + site + that day's secret salt). |
| Timeout | A session ends after 30 minutes with no request. |
| Hard cap | No session runs longer than 24 hours, whatever happens. |
| Assets | Images, CSS and scripts count toward activity but never start a session. |
| Across days | The salt changes nightly, so nobody can be followed across days unless visitor identity is switched on for that site. |
The consequence, stated plainly. Because the salt rotates nightly, one person visiting on Monday and again on Tuesday is two sessions and we cannot tell they were the same person. That is the cost of not tracking anyone, and it is a real cost rather than a detail. Sites that need returning visitor data can switch on visitor identity, which sets a first party cookie and moves that obligation to them.
If a CDN sits in front of your site and you send us origin logs, we see the requests that missed the cache. A page served from the edge to ten thousand people reaches your origin once.
Those numbers are ground truth for what reached your server. They are not ground truth for what reached your site, and we will not describe them as though they are. We detect the CDN from the headers it adds and say so on your dashboard rather than letting you find out later.
| How you send data | What is counted |
|---|---|
| Edge middleware or edge log stream | Everything, cache hits included. Complete. |
| Origin logs with no CDN | Everything, because every request reaches the origin. |
| Origin logs behind a CDN | Cache misses only. We flag this. |
A user agent string is free text and costs nothing to copy. Anything that claims a known identity is checked, in this order:
| Level | Check | How hard to forge |
|---|---|---|
| Signed | HTTP Message Signature, RFC 9421 | Requires the vendor's private key |
| In published range | Source address inside the list the vendor publishes | Requires controlling their address space |
| Reverse DNS confirmed | PTR resolves to the vendor and forward confirms back | Requires controlling their DNS |
| Unverified claim | A user agent string and nothing else | Trivial. Proves nothing. |
| Forged | Claims an identity and fails every check | Caught |
Forged is not the same as unknown. Traffic that asserts something checkable and fails is more informative than traffic that claims nothing, and we treat it that way.
When a vendor's published list has not been fetched successfully, that check is recorded as not run rather than as failed. A stale list must never be the reason a real crawler is called a forgery.
Residential proxy traffic is genuinely hard, and we are not going to pretend otherwise.
Address and network checks catch the cheap end: datacenter ranges, rented servers, obvious automation. They catch nothing arriving through a residential proxy pool, because those requests come from ordinary consumer ISPs with entirely plausible networks. Serious scraping moved there years ago.
What remains is behavioural, which is weaker: request graph shape, whether assets and conditional requests are fetched, timing that is too even to be human. Headless browsers increasingly spoof TLS and HTTP/2 fingerprints too.
Sessions judged mainly on those weaker signals are marked lower confidence rather than quietly counted as certain. Any product claiming to catch everything here is claiming something you could disprove in an afternoon.
The two systems disagree in both directions at once, which is why the totals can look close while the composition is completely wrong.
| Direction | Cause |
|---|---|
| GA4 counts too many | Headless browsers execute JavaScript exactly as a person does, so they arrive as users. |
| GA4 counts too few | Between a fifth and a third of real people block the script, and those visits never happened as far as it is concerned. |
| We count too few | Origin logs behind a CDN miss cache hits. See above. |
| We count differently | Our sessionisation rule is the one published above. GA4 uses its own, and the two are not interchangeable. |
A delta between the two only means something once both sessionisation rules are stated and coverage is equivalent. If anyone quotes you a percentage without doing that, including us, ask which rule produced it.
Method questions go to hello@clickbaton.com and are answered by a person who worked on it.