zylior
← Blog

First-party attribution: track your visitors without third-party cookies

You launch a campaign, you see 400 visitors in Google Analytics, 12 sign-ups… and you can't tell which ones came from which source. Welcome to attribution after third-party cookies, where 30 to 50% of your traffic has gone invisible. The good news: you don't need GA or third-party pixels. A pixel served from your own domain is enough to rebuild the chain visit → sign-up → revenue, and it survives everything that breaks the usual trackers.

Why the classic solutions collapse

Third-party pixels (Meta Pixel, Google's `gtag.js`, Hotjar…) set a cookie or read an identifier from a domain different from yours. That's exactly what browsers decided to kill. Three forces converge and you're already feeling them:

What these three breakages have in common: they target the third party. If the identifier comes from your domain and serves only you, most of these rules don't apply the same way.

The principle: a first-party pixel + an anonymous visitor_id

The idea comes down to three pieces. You serve a collection endpoint on your own domain (ideally a subdomain like `t.yourapp.com`, not an external tracking domain). On the first visit, you generate a random `visitor_id` and store it in a first-party, HttpOnly, Secure cookie. On every page view, the browser sends this cookie back to your server, which logs the event. The detail that changes everything: the cookie is set by a server-side HTTP `Set-Cookie` response, not by `document.cookie` in JavaScript. This is the nuance that gets around ITP's 7-day cap — the limit targets cookies set in JS, not those set by the server first-party. And `HttpOnly` makes them unreadable by any script, so useless for theft via XSS.

# Response from your /t/collect endpoint served on t.yourapp.com
HTTP/1.1 204 No Content
Set-Cookie: vid=8f3c1a9e-...; Max-Age=63072000; Path=/;
  Domain=.yourapp.com; HttpOnly; Secure; SameSite=Lax

// pixel.js client-side (~10 lines):
// fetch("https://t.yourapp.com/t/collect", {
//   method: "POST", credentials: "include", keepalive: true,
//   headers: { "Content-Type": "application/json" },
//   body: JSON.stringify({ path: location.pathname,
//     ref: document.referrer, utm: location.search, ts: Date.now() }) });

The merge: from anonymous visitor_id to proven identity

As long as the visitor is anonymous, you accumulate events tied to an opaque `visitor_id`. The magic happens at sign-up: the user proves their email (confirmation link or OTP), and you do the stitching — you link the cookie's `visitor_id` to the freshly created account. At that moment, the whole anonymous history from before (first visit, source, page views) attaches retroactively to a real identity.

  1. At sign-up, read the `vid` cookie server-side in the same request.
  2. Write a row `identity(visitor_id, user_id, email_verified_at)` — the email proven, not just typed in.
  3. Re-attach all anonymous events carrying that `visitor_id` to the `user_id`. You get the full path: Twitter → /pricing → /signup → email confirmed.
  4. Bonus: if the same email logs back in from another device, merge two `visitor_id`s under a single `user_id`.
NEVER merge on an email simply typed into a field. Wait for proof (confirmation/OTP). Otherwise a visitor who enters `competitor@gmail.com` pollutes the attribution of a real account — and you've just introduced an account-enumeration flaw. Email proof is the boundary between anonymous and identified.

What you gain vs Google Analytics and third-party pixels

This is not a license to track everything without consent. First-party reduces the technical risk and some banner requirements depending on purpose, but GDPR reasons by purpose and by case law (CNIL included). If you do marketing profiling, you need a legal basis. Keep the internal-measurement pixel separate from any advertising enrichment.

The traps that break the setup in production

Start small: one endpoint, one HttpOnly `Set-Cookie` served on your subdomain, an `events(visitor_id, path, ref, ts)` table and an `identity(visitor_id, user_id)` table populated when the email is proven. In one afternoon you'll have attribution that survives Safari, blockers and consent — and that plugs directly into your MRR. Once it's running, add multi-device and source counting. The rest is just SQL.

The newsletter

By subscribing you agree to receive the Zylior newsletter. One-click unsubscribe in every email.