Skip to content

Commit 6049804

Browse files
logaretmclaude
andauthored
fix(nextjs): skip custom browser tracing setup for bot user agents (#20263)
Exposes `isBotUserAgent()` from the browser tracing integration to use it in the Next.js browser tracing wrapper so bot user agents also skip Next.js-specific router instrumentation which adds its own interval timer. This is intentionally the smallest change to test whether this fixes the Googlebot rendering issue. Longer term, we probably want a more general mechanism for integrations to skip their own setup, with a skip decision that can be downstreamed easily through wrappers and integration variants instead of checking the bot predicate ad hoc in each package. closes #19670 Co-authored-by: GPT-5.4 <noreply@anthropic.com>
1 parent 13dc7a1 commit 6049804

10 files changed

+36
-3
lines changed

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
@@ -37,6 +37,7 @@ export { getFeedback, sendFeedback } from '@sentry-internal/feedback';
3737
export { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from './tracing/request';
3838
export {
3939
browserTracingIntegration,
40+
isBotUserAgent,
4041
startBrowserTracingNavigationSpan,
4142
startBrowserTracingPageLoadSpan,
4243
} 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)