Stealth mode plugin for puppeteer-extra that applies various techniques to make detection of headless browsers harder.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Evasion techniques that modify navigator properties to hide headless browser indicators. These techniques fix various navigator object properties that can reveal automation and make the browser appear more like a regular user's browser.
Removes or fixes the navigator.webdriver property that explicitly indicates automation.
// Evasion name: 'navigator.webdriver'
// Fixes: navigator.webdriver property detection
// Launch args: Adds --disable-blink-features=AutomationControlledThis evasion:
navigator.webdriver property in older Chrome versions--disable-blink-features=AutomationControlled launch argumentDetection Method Prevented:
// This detection method will be fooled:
if (navigator.webdriver) {
// Detects automation - webdriver property exists
} else {
// Assumes regular browser
}Fixes the navigator.languages array to match the Accept-Language header.
// Evasion name: 'navigator.languages'
// Fixes: Inconsistency between navigator.languages and Accept-Language headerThis evasion:
navigator.languages matches the Accept-Language headerDetection Method Prevented:
// This detection method will be fooled:
const languages = navigator.languages;
const acceptLanguage = /* from HTTP headers */;
if (languages.join(',') !== acceptLanguage) {
// Detects inconsistency indicating automation
}Fixes hardware concurrency reporting to match typical user systems.
// Evasion name: 'navigator.hardwareConcurrency'
// Fixes: navigator.hardwareConcurrency to report realistic CPU core countThis evasion:
Detection Method Prevented:
// This detection method will be fooled:
if (navigator.hardwareConcurrency === 1 || navigator.hardwareConcurrency > 16) {
// Detects unusual hardware suggesting automation environment
}Fixes permission API behavior, particularly for notifications.
// Evasion name: 'navigator.permissions'
// Fixes: Notification.permission and navigator.permissions.query() behaviorThis evasion:
Notification.permission behavior in headless modenavigator.permissions.query() returns expected resultsDetection Method Prevented:
// This detection method will be fooled:
navigator.permissions.query({name: 'notifications'}).then(result => {
if (result.state !== Notification.permission) {
// Detects permission API inconsistency
}
});Mocks browser plugins to simulate a regular browser environment.
// Evasion name: 'navigator.plugins'
// Fixes: Empty navigator.plugins array in headless modeThis evasion:
navigator.plugins array with common pluginsDetection Method Prevented:
// This detection method will be fooled:
if (navigator.plugins.length === 0) {
// Detects headless browser with no plugins
} else {
// Assumes regular browser with plugins
}Allows customization of the navigator.vendor property which is fixed in Puppeteer by default.
/**
* Navigator vendor evasion plugin
* @param opts - Configuration options
* @param opts.vendor - The vendor string to use (default: 'Google Inc.')
* @returns Plugin instance
*/
function NavigatorVendorPlugin(opts?: {
vendor?: string;
}): Plugin;
// Evasion name: 'navigator.vendor'
// Fixes: Fixed navigator.vendor property in headless mode
// Configuration: Accepts custom vendor stringThis evasion:
navigator.vendor propertyConfiguration Options:
// Use default vendor (Google Inc.)
const vendorPlugin = NavigatorVendorPlugin();
// Use custom vendor
const appleVendor = NavigatorVendorPlugin({
vendor: 'Apple Computer, Inc.'
});Detection Method Prevented:
// This detection method will be fooled:
if (navigator.vendor === 'Google Inc.' && /* other headless indicators */) {
// Might detect specific headless patterns
} else {
// Custom vendor helps avoid detection patterns
}Usage Examples:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
// Enable only navigator-related evasions
const navigatorStealth = StealthPlugin({
enabledEvasions: new Set([
'navigator.webdriver',
'navigator.languages',
'navigator.hardwareConcurrency',
'navigator.permissions',
'navigator.plugins',
'navigator.vendor'
])
});
puppeteer.use(navigatorStealth);
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Navigator properties will now appear normal
await page.evaluate(() => {
console.log(navigator.webdriver); // undefined (not true)
console.log(navigator.languages); // ['en-US', 'en']
console.log(navigator.hardwareConcurrency); // 4 (realistic number)
console.log(navigator.plugins.length); // > 0 (has plugins)
console.log(navigator.vendor); // 'Google Inc.' (customizable)
// Permissions work normally
return navigator.permissions.query({name: 'notifications'});
});Navigator evasions work together with other plugin components:
The navigator.languages evasion coordinates with user-agent-override to ensure:
The navigator.webdriver evasion adds launch arguments:
--disable-blink-features=AutomationControlledEach navigator evasion:
page.evaluateOnNewDocument() for immediate executionNavigator evasions are designed for:
// Enable specific navigator evasions with custom values
const customNavigator = StealthPlugin({
enabledEvasions: new Set(['navigator.webdriver', 'navigator.plugins', 'navigator.vendor'])
});
// For individual evasion plugins with custom options:
const customVendor = require('puppeteer-extra-plugin-stealth/evasions/navigator.vendor');
puppeteer.use(customVendor({ vendor: 'Apple Computer, Inc.' }));// Check which navigator properties are being modified
await page.evaluate(() => {
const props = [
'webdriver', 'languages', 'hardwareConcurrency',
'permissions', 'plugins', 'vendor'
];
return props.map(prop => ({
property: prop,
value: navigator[prop],
type: typeof navigator[prop]
}));
});Install with Tessl CLI
npx tessl i tessl/npm-puppeteer-extra-plugin-stealth