IP handling
How an IP address becomes a visitor hash and never touches storage.
The IP address is the most sensitive value in any analytics system. Sift uses it once, in memory, and then lets it go.
The path of one request
- A pageview arrives at
/api/event. - The IP is read from
cf-connecting-ip, falling back to the first entry inx-forwarded-for. - The IP, the user agent, the site ID, and today's salt are concatenated and hashed with SHA-256.
- The same is done with yesterday's salt, so sessions that cross midnight keep working.
- The first 8 bytes of each digest become a 64-bit integer — the visitor ID.
- The IP and user agent go out of scope. Nothing writes them anywhere.
The stored pageview contains the visitor ID and the parsed browser, OS, device, country, region, and city. It does not contain the IP or the raw user agent.
Why a hash and not encryption
Encryption is reversible by whoever holds the key. A hash is not.
Even with the database, the salt table, and the source code, you cannot turn a visitor ID back into an IP address without guessing the exact IP and user agent pair and re-hashing it. The search space makes that impractical for real traffic.
Why the salt rotates daily
If the salt never changed, the same person would produce the same hash forever. That is a persistent identifier, and it is exactly what Sift avoids.
With a daily salt:
- Yesterday's hash and today's hash are unrelated.
- A daily unique count is meaningful.
- A monthly unique count is a sum of daily uniques, not a count of people.
- Cross-day tracking is impossible, by construction.
Why yesterday's salt is kept
A session that starts at 23:50 and continues past midnight must not split into two visitors. Sift computes both the current and previous day's hash and matches either.
This only affects sessions that span midnight. It does not create a link between otherwise separate visits.
Trusting the IP header
Behind a proxy or CDN, cf-connecting-ip is authoritative on Cloudflare. On
other setups, x-forwarded-for is only as trustworthy as the proxy that sets it.
If you proxy the script, forward the original IP header. Strip it and every visitor collapses into one, which looks like a tracking outage and is really a configuration mistake.
What is not done
- No geolocation by IP lookup in application code. Country, region, and city come from the edge request, when the platform provides them.
- No IP-based blocking or rate limiting that stores the address.
- No application logging of the IP address. The hosting platform may keep its own request logs; that is outside Sift's control and worth checking in your provider's terms.