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.
This commit is contained in:
Nolann Biron
2026-09-15 17:20:25 +02:00
parent 29b89a0b4a
commit 426c58671d
6 changed files with 14 additions and 20 deletions
@@ -18,10 +18,9 @@ export default async function SiteDynamicLayout({
params,
children,
}: React.PropsWithChildren<SiteDynamicLayoutProps>) {
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 (
<CustomizationRootLayout
@@ -19,7 +19,7 @@ export default async function RootLayout({
}: React.PropsWithChildren<SiteStaticLayoutProps>) {
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;
@@ -15,9 +15,8 @@ export default async function SiteStaticLayout({
params,
children,
}: React.PropsWithChildren<SiteStaticLayoutProps>) {
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 (
<CustomizationRootLayout
@@ -17,7 +17,7 @@ export default async function RootLayout({
}: React.PropsWithChildren<SiteStaticLayoutProps>) {
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;
+3 -12
View File
@@ -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<ReturnType<typeof nextHeaders>>
): boolean {
export function shouldTrackEvents(headers?: Awaited<ReturnType<typeof nextHeaders>>): 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;
}
+5
View File
@@ -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);
}