Cookieless pageview collector
A tracking endpoint that identifies visitors with a daily-rotating salted hash instead of cookies, so no personal data is ever stored.
LaravelAPIsanalyticsprivacycookielesshashing
Code
How it works
What it solves
Cookie banners exist because analytics scripts store identifiers. You can count unique visitors without any storage on the device by deriving an anonymous id on the server, and discarding it before it can be linked across days.
How it works
VisitorHasher hashes the site id, a normalised IP and the lowercased user agent together with a secret salt. The salt is created fresh each UTC day and kept only for that day, so the same person gets a different id tomorrow and old ids can never be recomputed once the salt is deleted. IPv6 addresses are cut to their /64 so privacy extensions do not inflate the count. The IP and user agent are inputs only and are never saved.
CollectController validates a small payload (path, referrer, an optional screen width), builds the hash, and inserts a row with the hash, path and timestamp. Salts live in a daily_salts table; firstOrCreate makes the first request of the day generate one, and a nightly job deletes salts older than a day.
The endpoint returns 202 with no body, which suits navigator.sendBeacon.
Adapting it
Add a site key lookup, a rate limit and bot filtering before the insert. Roll the salt at midnight in the site's timezone if you want local-day uniques.
Pitfalls
- Never log the raw IP in access logs for this route if you promise not to store it.
- Behind a proxy, configure trusted proxies so
ip()is the client, not the load balancer.
How to use it
- Create
daily_salts(day date unique, salt string) andpageviews(site_id, visitor, path, referrer, created_at). - Copy both classes; route
Route::post('/collect', CollectController::class)->middleware('throttle:600,1'). - Send from the browser with
navigator.sendBeacon('/collect', JSON.stringify({site: 1, path: location.pathname, ref: document.referrer})). - Schedule
DB::table('daily_salts')->where('day', '<', today())->delete()nightly. - Count uniques with
distinct visitor.
Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.