Skip to content

Commit e37632a

Browse files
committed
bot detection
1 parent cd05479 commit e37632a

2 files changed

Lines changed: 89 additions & 0 deletions

File tree

packages/browser/src/tracing/browserTracingIntegration.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ import { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from
5454

5555
export const BROWSER_TRACING_INTEGRATION_ID = 'BrowserTracing';
5656

57+
/**
58+
* We don't want to start a bunch of idle timers and PerformanceObservers
59+
* for web crawlers, as they may prevent the page from being seen as "idle"
60+
* by the crawler's rendering engine (e.g. Googlebot's headless Chromium).
61+
*/
62+
const BOT_USER_AGENT_RE =
63+
/Googlebot|Google-InspectionTool|Storebot-Google|Bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot|Facebot|facebookexternalhit|LinkedInBot|Twitterbot|Applebot/i;
64+
65+
function _isBotUserAgent(): boolean {
66+
const nav = WINDOW.navigator as Navigator | undefined;
67+
if (!nav?.userAgent) {
68+
return false;
69+
}
70+
return BOT_USER_AGENT_RE.test(nav.userAgent);
71+
}
72+
5773
interface RouteInfo {
5874
name: string | undefined;
5975
source: TransactionSource | undefined;
@@ -384,6 +400,8 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
384400
...options,
385401
};
386402

403+
const _isBot = _isBotUserAgent();
404+
387405
let _collectWebVitals: undefined | (() => void);
388406
let lastInteractionTimestamp: number | undefined;
389407

@@ -484,6 +502,11 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
484502
return {
485503
name: BROWSER_TRACING_INTEGRATION_ID,
486504
setup(client) {
505+
if (_isBot) {
506+
DEBUG_BUILD && debug.log('[Tracing] Skipping browserTracingIntegration setup for bot user agent.');
507+
return;
508+
}
509+
487510
registerSpanErrorInstrumentation();
488511

489512
_collectWebVitals = startTrackingWebVitals({
@@ -630,6 +653,10 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
630653
},
631654

632655
afterAllSetup(client) {
656+
if (_isBot) {
657+
return;
658+
}
659+
633660
let startingUrl: string | undefined = getLocationHref();
634661

635662
if (linkPreviousTrace !== 'off') {

packages/browser/test/tracing/browserTracingIntegration.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,68 @@ describe('browserTracingIntegration', () => {
8686
Object.defineProperty(WINDOW, 'history', { value: originalGlobalHistory });
8787
});
8888

89+
describe('bot user agent detection', () => {
90+
let originalNavigator: Navigator;
91+
92+
beforeEach(() => {
93+
originalNavigator = WINDOW.navigator;
94+
});
95+
96+
afterEach(() => {
97+
Object.defineProperty(WINDOW, 'navigator', { value: originalNavigator, writable: true, configurable: true });
98+
});
99+
100+
function setUserAgent(ua: string): void {
101+
Object.defineProperty(WINDOW, 'navigator', {
102+
value: { userAgent: ua },
103+
writable: true,
104+
configurable: true,
105+
});
106+
}
107+
108+
it.each([
109+
'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
110+
'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/W.X.Y.Z Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
111+
'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Bingbot/2.0; +http://www.bing.com/bingbot.htm) Chrome/W.X.Y.Z Safari/537.36',
112+
'Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)',
113+
'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)',
114+
'LinkedInBot/1.0 (compatible; Mozilla/5.0)',
115+
'Twitterbot/1.0',
116+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15 (Applebot/0.1)',
117+
'Mozilla/5.0 (compatible; Google-InspectionTool/1.0)',
118+
])('skips tracing setup for bot user agent: %s', ua => {
119+
setUserAgent(ua);
120+
121+
const client = new BrowserClient(
122+
getDefaultBrowserClientOptions({
123+
tracesSampleRate: 1,
124+
integrations: [browserTracingIntegration()],
125+
}),
126+
);
127+
setCurrentClient(client);
128+
client.init();
129+
130+
expect(getActiveSpan()).toBeUndefined();
131+
});
132+
133+
it('does not skip tracing setup for normal user agents', () => {
134+
setUserAgent(
135+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
136+
);
137+
138+
const client = new BrowserClient(
139+
getDefaultBrowserClientOptions({
140+
tracesSampleRate: 1,
141+
integrations: [browserTracingIntegration()],
142+
}),
143+
);
144+
setCurrentClient(client);
145+
client.init();
146+
147+
expect(getActiveSpan()).toBeDefined();
148+
});
149+
});
150+
89151
it('works with tracing enabled', () => {
90152
const client = new BrowserClient(
91153
getDefaultBrowserClientOptions({

0 commit comments

Comments
 (0)