First-party attribution for Next.js
Next.js gives you middleware that runs on every request at the edge — the ideal place to mint a durable, server-set visitor id before a single byte of React ships. Most Next.js sites instead load a client-side tag in `app/layout.tsx` and inherit every ITP problem for free.
Durable id (server-set) + client events (behaviour) + server events (truth) + dedup key
Middleware sets the id cookie; a route handler proxies events; server actions confirm conversions. The one decision that matters on Next.js: who writes the visitor id. Written by JavaScript it lives 7 days in Safari; written by a server it lasts as long as you set it.
Use `middleware.ts` to mint the id, and a `app/api/events/route.ts` handler to forward events to the attribution API — that is proxy mode, and it is ITP-proof.
What Next.js gives you for free
Every stack has a natural advantage for first-party measurement. Use it before reaching for a tag manager.
Middleware runs before render, on your domain — perfect for a server-set HttpOnly cookie
Route handlers let you proxy events through your own origin, so nothing cross-origin is blocked
Server actions give you an authoritative place to confirm conversions
What breaks on Next.js
The failure modes specific to this stack — the ones a generic install guide will not warn you about.
Client-side navigation fires no pageview
The App Router changes routes without a document load, so a naive tag records only the first page.
Fix Hook the router events, or use a tracker that patches history navigation.
Static generation caches the cookie
A statically generated page cannot set a per-visitor cookie.
Fix Set it in middleware, which runs per request even for static pages.
Double-firing in React Strict Mode
Effects run twice in development, sending every event twice.
Fix Guard on a module-level flag, and send a dedup key so duplicates collapse anyway.
Frequently asked questions
Where should I set the visitor cookie in Next.js?
In `middleware.ts`. It runs on every request before rendering, on your own domain, so the cookie goes out in an HTTP response header — durable in Safari, invisible to ad blockers.
How do I track App Router navigations?
Subscribe to route changes (`usePathname` + `useSearchParams` in a client component, or patch `history.pushState`) and emit a pageview on each change. A document-load-only tag will undercount badly.
Why proxy events through my own API route?
Because a POST to your own origin is not a cross-origin request, is not on any tracker blocklist, and cannot be blocked by third-party request filtering. You forward server-to-server from there.
Does this work with the Pages Router?
Yes. Use `middleware.ts` the same way and hook `router.events.on('routeChangeComplete')` for navigations.
Related guides
See the receipt for every conversion.
Flowsk Signals stitches the anonymous click to the email to the purchase — first-party, server-side, de-duplicated on a key you choose. One snippet, $29/mo, and a journey you can inspect event by event.
More free tools
Same deal — instant, no signup.