From 426c58671d67cac372c87bcfb31ddfcc668de593 Mon Sep 17 00:00:00 2001 From: Nolann Biron Date: Tue, 15 Sep 2026 17:20:25 +0200 Subject: [PATCH] Exclude /url/ traffic from analytics at the events endpoint Gating it on the layout's withTracking flag also dropped every third-party script, which removed the cookie banner and broke the e2e suite. --- .../[siteURL]/[siteData]/(content)/layout.tsx | 5 ++--- .../[siteData]/~gitbook/embed/layout.tsx | 2 +- .../[siteURL]/[siteData]/(content)/layout.tsx | 5 ++--- .../[siteData]/~gitbook/embed/layout.tsx | 2 +- packages/gitbook/src/lib/tracking.ts | 15 +++------------ packages/gitbook/src/middleware.ts | 5 +++++ 6 files changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/(content)/layout.tsx b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/(content)/layout.tsx index 340b77cce..7129cfba1 100644 --- a/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/(content)/layout.tsx +++ b/packages/gitbook/src/app/sites/dynamic/[mode]/[siteURL]/[siteData]/(content)/layout.tsx @@ -18,10 +18,9 @@ export default async function SiteDynamicLayout({ params, children, }: React.PropsWithChildren) { - const resolvedParams = await params; - const { context, visitorAuthClaims } = await getDynamicSiteContext(resolvedParams); + const { context, visitorAuthClaims } = await getDynamicSiteContext(await params); const forcedTheme = await getThemeFromMiddleware(); - const withTracking = shouldTrackEvents(resolvedParams.mode, await headers()); + const withTracking = shouldTrackEvents(await headers()); return ( ) { const resolvedParams = await params; const { context, visitorAuthClaims } = await getEmbeddableStaticContext(resolvedParams); - const withTracking = shouldTrackEvents(resolvedParams.mode, await headers()); + const withTracking = shouldTrackEvents(await headers()); // The forced theme (`?theme=`) comes through the route context (set by the middleware), not a // request header, so the embed can honor it while staying statically rendered. RND-11571 const forcedTheme = getSiteURLDataFromParams(resolvedParams).embedTheme ?? null; diff --git a/packages/gitbook/src/app/sites/static/[mode]/[siteURL]/[siteData]/(content)/layout.tsx b/packages/gitbook/src/app/sites/static/[mode]/[siteURL]/[siteData]/(content)/layout.tsx index d7971da22..b86e8a387 100644 --- a/packages/gitbook/src/app/sites/static/[mode]/[siteURL]/[siteData]/(content)/layout.tsx +++ b/packages/gitbook/src/app/sites/static/[mode]/[siteURL]/[siteData]/(content)/layout.tsx @@ -15,9 +15,8 @@ export default async function SiteStaticLayout({ params, children, }: React.PropsWithChildren) { - const resolvedParams = await params; - const { context, visitorAuthClaims } = await getStaticSiteContext(resolvedParams); - const withTracking = shouldTrackEvents(resolvedParams.mode); + const { context, visitorAuthClaims } = await getStaticSiteContext(await params); + const withTracking = shouldTrackEvents(); return ( ) { const resolvedParams = await params; const { context, visitorAuthClaims } = await getEmbeddableStaticContext(resolvedParams); - const withTracking = shouldTrackEvents(resolvedParams.mode); + const withTracking = shouldTrackEvents(); // The forced theme (`?theme=`) is threaded through the route context by the middleware so the // embed stays statically rendered — read it from the params rather than a request header. RND-11571 const forcedTheme = getSiteURLDataFromParams(resolvedParams).embedTheme ?? null; diff --git a/packages/gitbook/src/lib/tracking.ts b/packages/gitbook/src/lib/tracking.ts index 1b1b2da55..22dfd14d6 100644 --- a/packages/gitbook/src/lib/tracking.ts +++ b/packages/gitbook/src/lib/tracking.ts @@ -9,24 +9,15 @@ import { getLogger } from './logger'; /** * Return true if events should be tracked on the site. * Can be called from the static context or the dynamic context. - * In the static context, only the env variable and the serving mode are checked. - * In the dynamic context, the request headers are checked too - this allows the middleware + * In the static context, only an env variable is checked. + * In the dynamic context, the request headers are checked - this allows the middleware * to disable tracking for preview requests. */ -export function shouldTrackEvents( - mode: string, - headers?: Awaited> -): boolean { +export function shouldTrackEvents(headers?: Awaited>): boolean { if (GITBOOK_DISABLE_TRACKING) { return false; } - // `url` mode only serves `/url/:url` on GitBook's own host — local dev and preview - // deployments. That traffic is not the site's, so it must stay out of its analytics. - if (mode === 'url') { - return false; - } - if (headers?.get('x-gitbook-disable-tracking') === 'true') { return false; } diff --git a/packages/gitbook/src/middleware.ts b/packages/gitbook/src/middleware.ts index db771a507..ed2daf0aa 100644 --- a/packages/gitbook/src/middleware.ts +++ b/packages/gitbook/src/middleware.ts @@ -185,6 +185,11 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) { //Forwards analytics events if (siteRequestURL.pathname.endsWith('/~gitbook/__evt')) { + // `url` mode only serves `/url/:url` on GitBook's own host — local dev and preview + // deployments. That traffic is not the site's, so it stays out of its analytics. + if (mode === 'url') { + return new Response(null, { status: 204 }); + } return await serveProxyAnalyticsEvent(request); }