# Cookieless visitor counting

How ProductLense counts unique visitors without cookies or stored IP addresses, using a salted daily hash, and what that means for the numbers.

ProductLense counts unique visitors without cookies, without storing anything on the visitor's device, and without keeping IP addresses. Each visitor gets an id that lives for one day and cannot be reversed.

## The visitor id

```text
visitor_id = first 16 hex characters of sha256(site_id | ip | user_agent | daily_salt)
```

- `daily_salt` is 32 random bytes generated for each UTC day. It is not derived from anything, so it cannot be recreated.
- Salts are kept only while needed: yesterday's, today's and tomorrow's are held, and older ones are deleted. Once a day's salt is gone, nobody, including ProductLense, can recompute that day's ids from an IP address.
- The IP address is used in memory for the hash and the country lookup and then dropped.
- The site id is in the hash, so the same browser on two ProductLense sites has two unrelated ids.

## What this means for your numbers

- The same browser on the same day keeps one id, so sessions and same-day journeys work.
- The same browser tomorrow gets a new id. A person who visits on three days counts as three visitors in a weekly total.
- Two people behind the same IP address with identical browsers on the same day count as one visitor.
- A phone switching between Wi-Fi and mobile data changes IP address and starts a new visitor.

`identify` is how visits on different days, devices or networks join: once a visitor calls it with your user id, every visitor id linked to that user belongs to one person for attribution. See [Identify users](/docs/track/identify-users).

## What it means for revenue attribution

Without a long-lived identifier, nothing in the browser can say that today's buyer is the same person who clicked your ad three weeks ago. ProductLense closes that gap at signup instead of with a cookie:

- **Same day:** the visitor id is stable, so a visitor who lands from a campaign and pays the same day is attributed with no extra work.
- **Across days:** call [`identify`](/docs/track/identify-users) when someone signs up or logs in, and put the same user id on the billing customer. Every visitor id linked to that user then shares the earliest first touch, so a payment months later still credits the campaign that brought them.
- **Without identify:** a visitor who first came from a campaign, left, and returned another day to pay can only be linked to the visit on the day they paid, so the payment is credited to that day's channel, most often `direct` or `organic_search`, or stays unattributed when no visit can be linked at all.

This is a real limit of every cookieless tool, not a setting. For products with a signup, `identify` removes most of it; see [How payments are attributed](/docs/revenue/attribution).

## Do you need a cookie banner?

ProductLense sets no cookies and stores nothing on the device before `identify`, which keeps one user id in `sessionStorage` for the tab. Under the ePrivacy rules that usually means no consent banner is needed for ProductLense. The rules and their interpretation differ between countries, and you remain responsible for your privacy notice; see [GDPR and India's DPDP Act](/docs/privacy/gdpr-and-dpdp).

## Sessions

A session ends after 30 minutes without an event. Because the visitor id changes at UTC midnight, a session running across midnight UTC becomes two.

## Compared with cookies

| | ProductLense | Cookie-based analytics |
|---|---|---|
| Stored on the device | Nothing (a user id in `sessionStorage` only after `identify`) | A long-lived identifier |
| Returning visitor across days | Counted again, unless identified | Recognised until the cookie is cleared |
| IP address | Hashed with a daily salt, never stored | Often stored or truncated |

Unique counts in long ranges are also approximated with HyperLogLog for speed, within about 0.8% to 1.6%; see [How ProductLense counts](/docs/get-started/how-productlense-counts#where-counts-are-approximate).
