fix(analytics): identify page views via fetch metadata, not router headers
Next.js strips the router-internal headers (RSC, next-router-prefetch) before the request proxy runs, so prefetch detection based on them never fired: link prefetches were counted as page views. A page view is now a browser navigation, identified by the standard sec-fetch-dest: document header, which survives into the proxy. Prefetch payloads, subresource fetches and metadata-less clients are skipped as non-navigation requests.
This commit is contained in:
+5
-7
@@ -3,6 +3,10 @@
|
||||
* the pure domain decision, and records a page view without touching the
|
||||
* response. All analytics happen server-side; no tracking script ships to
|
||||
* the browser.
|
||||
*
|
||||
* Page views are browser navigations (`sec-fetch-dest: document`).
|
||||
* Router-internal headers (RSC, next-router-prefetch) are stripped by the
|
||||
* server before this shell runs and cannot be used here.
|
||||
*/
|
||||
|
||||
import { NextResponse, type NextRequest } from "next/server";
|
||||
@@ -18,14 +22,10 @@ const firstForwardedIp = (forwardedFor: string | null): string =>
|
||||
|
||||
const hostnameWithoutPort = (host: string): string => host.replace(/:\d+$/, "");
|
||||
|
||||
const isPrefetchRequest = (request: NextRequest): boolean =>
|
||||
request.headers.get("next-router-prefetch") === "1" ||
|
||||
request.headers.get("purpose") === "prefetch" ||
|
||||
request.headers.get("sec-purpose") === "prefetch";
|
||||
|
||||
export default function proxy(request: NextRequest): NextResponse {
|
||||
const decision = decidePageview({
|
||||
method: request.method,
|
||||
secFetchDest: request.headers.get("sec-fetch-dest") ?? "",
|
||||
pathname: request.nextUrl.pathname,
|
||||
search: request.nextUrl.search,
|
||||
hostname: hostnameWithoutPort(
|
||||
@@ -37,8 +37,6 @@ export default function proxy(request: NextRequest): NextResponse {
|
||||
language: primaryLanguage(request.headers.get("accept-language")),
|
||||
userAgent: request.headers.get("user-agent") ?? "",
|
||||
clientIp: firstForwardedIp(request.headers.get("x-forwarded-for")),
|
||||
isRscNavigation: request.headers.get("rsc") === "1",
|
||||
isPrefetch: isPrefetchRequest(request),
|
||||
});
|
||||
|
||||
if (decision.track) {
|
||||
|
||||
Reference in New Issue
Block a user