Skip to content

Commit 7f06fe1

Browse files
logaretmclaude
andcommitted
fix(nextjs): skip custom browser tracing setup for bot user agents
Expose isBotUserAgent from the browser tracing integration and use it in the Next.js wrapper so bot user agents also skip the Next.js-specific router instrumentation path. This is the smallest change to validate the fix. Longer term, we probably want a more general mechanism for integrations to skip their own setup and make that skip decision easy to downstream through wrappers and variants. Co-Authored-By: GPT-5.4 <noreply@anthropic.com>
1 parent 6b5e16f commit 7f06fe1

10 files changed

Lines changed: 36 additions & 3 deletions

packages/browser/src/index.bundle.tracing.logs.metrics.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export {
2222

2323
export {
2424
browserTracingIntegration,
25+
isBotUserAgent,
2526
startBrowserTracingNavigationSpan,
2627
startBrowserTracingPageLoadSpan,
2728
} from './tracing/browserTracingIntegration';

packages/browser/src/index.bundle.tracing.replay.feedback.logs.metrics.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export {
2222

2323
export {
2424
browserTracingIntegration,
25+
isBotUserAgent,
2526
startBrowserTracingNavigationSpan,
2627
startBrowserTracingPageLoadSpan,
2728
} from './tracing/browserTracingIntegration';

packages/browser/src/index.bundle.tracing.replay.feedback.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export {
2727

2828
export {
2929
browserTracingIntegration,
30+
isBotUserAgent,
3031
startBrowserTracingNavigationSpan,
3132
startBrowserTracingPageLoadSpan,
3233
} from './tracing/browserTracingIntegration';

packages/browser/src/index.bundle.tracing.replay.logs.metrics.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export {
2222

2323
export {
2424
browserTracingIntegration,
25+
isBotUserAgent,
2526
startBrowserTracingNavigationSpan,
2627
startBrowserTracingPageLoadSpan,
2728
} from './tracing/browserTracingIntegration';

packages/browser/src/index.bundle.tracing.replay.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export {
2727

2828
export {
2929
browserTracingIntegration,
30+
isBotUserAgent,
3031
startBrowserTracingNavigationSpan,
3132
startBrowserTracingPageLoadSpan,
3233
} from './tracing/browserTracingIntegration';

packages/browser/src/index.bundle.tracing.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export {
2828

2929
export {
3030
browserTracingIntegration,
31+
isBotUserAgent,
3132
startBrowserTracingNavigationSpan,
3233
startBrowserTracingPageLoadSpan,
3334
} from './tracing/browserTracingIntegration';

packages/browser/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export { getFeedback, sendFeedback } from '@sentry-internal/feedback';
3636
export { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './tracing/request';
3737
export {
3838
browserTracingIntegration,
39+
isBotUserAgent,
3940
startBrowserTracingNavigationSpan,
4041
startBrowserTracingPageLoadSpan,
4142
} from './tracing/browserTracingIntegration';

packages/browser/src/tracing/browserTracingIntegration.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing';
6262
const BOT_USER_AGENT_RE =
6363
/Googlebot|Google-InspectionTool|Storebot-Google|Bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot|Facebot|facebookexternalhit|LinkedInBot|Twitterbot|Applebot/i;
6464

65-
function _isBotUserAgent(): boolean {
65+
export function isBotUserAgent(): boolean {
6666
const nav = WINDOW.navigator as Navigator | undefined;
6767
if (!nav?.userAgent) {
6868
return false;
@@ -405,7 +405,7 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
405405
...options,
406406
};
407407

408-
const _isBot = _isBotUserAgent();
408+
const _isBot = isBotUserAgent();
409409

410410
let _collectWebVitals: undefined | (() => void);
411411
let lastInteractionTimestamp: number | undefined;

packages/nextjs/src/client/browserTracingIntegration.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Integration } from '@sentry/core';
2-
import { browserTracingIntegration as originalBrowserTracingIntegration } from '@sentry/react';
2+
import { browserTracingIntegration as originalBrowserTracingIntegration, isBotUserAgent } from '@sentry/react';
33
import { nextRouterInstrumentNavigation, nextRouterInstrumentPageLoad } from './routing/nextRoutingInstrumentation';
44

55
/**
@@ -29,6 +29,10 @@ export function browserTracingIntegration(
2929
return {
3030
...browserTracingIntegrationInstance,
3131
afterAllSetup(client) {
32+
if (isBotUserAgent()) {
33+
return;
34+
}
35+
3236
// We need to run the navigation span instrumentation before the `afterAllSetup` hook on the normal browser
3337
// tracing integration because we need to ensure the order of execution is as follows:
3438
// Instrumentation to start span on RSC fetch request runs -> Instrumentation to put tracing headers from active span on fetch runs

packages/nextjs/test/clientSdk.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ Object.defineProperty(global, 'addEventListener', { value: () => undefined, writ
1919

2020
const originalGlobalDocument = WINDOW.document;
2121
const originalGlobalLocation = WINDOW.location;
22+
const originalNavigator = WINDOW.navigator;
2223
// eslint-disable-next-line @typescript-eslint/unbound-method
2324
const originalGlobalAddEventListener = WINDOW.addEventListener;
2425

2526
afterAll(() => {
2627
// Clean up JSDom
2728
Object.defineProperty(WINDOW, 'document', { value: originalGlobalDocument });
2829
Object.defineProperty(WINDOW, 'location', { value: originalGlobalLocation });
30+
Object.defineProperty(WINDOW, 'navigator', { value: originalNavigator, writable: true, configurable: true });
2931
Object.defineProperty(WINDOW, 'addEventListener', { value: originalGlobalAddEventListener });
3032
});
3133

@@ -43,6 +45,7 @@ describe('Client init()', () => {
4345
getIsolationScope().clear();
4446
getCurrentScope().clear();
4547
getCurrentScope().setClient(undefined);
48+
Object.defineProperty(WINDOW, 'navigator', { value: originalNavigator, writable: true, configurable: true });
4649
});
4750

4851
it('inits the React SDK', () => {
@@ -160,6 +163,25 @@ describe('Client init()', () => {
160163
// @ts-expect-error Test setup for build-time flag
161164
delete globalThis.__SENTRY_TRACING__;
162165
});
166+
167+
it("doesn't run Next.js router instrumentation for bot user agents", () => {
168+
Object.defineProperty(WINDOW, 'navigator', {
169+
value: {
170+
userAgent: 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
171+
},
172+
writable: true,
173+
configurable: true,
174+
});
175+
176+
const setIntervalSpy = vi.spyOn(globalThis, 'setInterval');
177+
178+
init({
179+
dsn: TEST_DSN,
180+
tracesSampleRate: 1.0,
181+
});
182+
183+
expect(setIntervalSpy).not.toHaveBeenCalled();
184+
});
163185
});
164186
});
165187

0 commit comments

Comments
 (0)