How cookie-free visitor counting actually works
· 4 min read
The classic way to count "unique visitors" is to give each browser a cookie with a random ID. Come back tomorrow, next week, next year, and the cookie says you are the same person. It works well, which is exactly the problem. The same mechanism that counts you also follows you, and under European law it needs consent before it is set.
Coldreader has no cookies, sets nothing in local storage, and does not fingerprint browsers. It still shows a visitor count. This post explains how, and what you give up.
What the browser sends
When a page loads, our script sends a tiny request with four fields: the site ID, the page path, the referrer, and the window width. Nothing else. No identifiers, no timestamps of previous visits, nothing read from storage.
The request itself, like every HTTP request, arrives with an IP address and a user agent string. Those two facts are what we use, briefly.
The daily hash
On the server we take the site ID, the IP address, the user agent, and a random salt, concatenate them, and run them through SHA-256. The result is a short hex string. We keep the first twenty characters as the "visitor" value for that pageview. The IP address and user agent are discarded from memory in the same millisecond and are never written to the database, to logs, or anywhere else.
The salt is the important part. It is 32 random bytes generated fresh every day. Yesterday's salt is deleted when today's is created. That means:
- Two pageviews from the same browser on the same day produce the same hash, so we can count that browser once.
- The same browser tomorrow produces a completely different hash, because the salt changed.
- Nobody can take a hash and work backwards to an IP address, because the salt it was made with no longer exists. Not us, not an attacker with a copy of the database, not a court order.
It is a one-way, one-day identifier that self-destructs.
What "visitors" means as a result
"Visitors" in Coldreader means distinct browsers per day. A person who reads three pages today counts once. The same person tomorrow counts again as a new visitor. Over a thirty-day range, "visitors" is the sum of daily uniques, which is higher than the number of distinct humans.
We think this is the right number for most sites, because most questions are "how many people came this week" rather than "how many of them had been here before". But it is an honest limitation and you should know it.
What we cannot tell you
- Returning versus new visitors. Not possible without remembering something across days, which is exactly what we refuse to do.
- Funnels across days. Someone reading your blog on Monday and signing up on Thursday is two unrelated visitors to us.
- Per-user anything. No user timelines, no session replays, no "this person viewed these seven pages". The data does not exist.
If your business depends on those, you need a consent-based tool for the part of your traffic that agrees to it. Many customers run Coldreader for the honest overall picture and something heavier behind a consent prompt for the small opted-in slice.
Edge cases worth knowing
Shared IP addresses. An office of fifty people on one IP, all using the same browser version, would collapse into one visitor. In practice user agents differ enough that this is rare, but corporate networks do undercount a little.
Carrier-grade NAT on mobile. Mobile networks put many phones behind a few IPs, and phones tend to run the same OS version. Some undercounting happens here too. We have measured it against cookie-based tools and it is in the low single-digit percent for typical sites.
Privacy relays. Apple's iCloud Private Relay and some VPNs rotate IPs within a day, which can count one person twice. It roughly cancels the effect above.
None of these is a reason to add a cookie. They are small, they are stable, and they do not hide trends, which is what you are actually looking at.
What else is stored
Per pageview: timestamp, site ID, path without query string, the referrer's hostname only, the visitor hash, and four coarse buckets: device class, browser family, operating system family, and a screen-width range. Country is looked up from the IP range in memory before the IP is discarded, and stored as a two-letter code.
No query strings, so no accidental email addresses or tokens from URLs. No full referrer URLs, so no search terms or private page paths from other sites. No exact screen sizes, because a 1440 by 900 window on a particular OS and browser is most of a fingerprint.
Why bother
Because the alternative is a banner. Every consent banner is a small tax on every visitor's attention and a large tax on the site owner's data, since a good share of people click "reject". A tool that needs no consent has no such gap. What you see is everyone.
We would rather show you a slightly imperfect count of all your visitors than a precise count of the half who clicked accept.