Puppeteer Browser Automation with Fingerprint Profiles
Complete guide to integrating Puppeteer with fingerprint-consistent browser profiles, proxy support, and production deployment.
Introduction
Puppeteer is Google's Node.js library for controlling Chrome through the Chrome DevTools Protocol (CDP). It provides a high-level API for navigation, screenshot capture, form interaction, network interception, and more. BotBrowser replaces the standard Chromium binary with one that produces consistent fingerprint output controlled by profile files. Puppeteer handles automation, BotBrowser handles identity.
This guide covers the complete integration: installation, launch configuration, viewport management, proxy setup, CDP access, production deployment on Linux servers, and common troubleshooting. By the end, you will have a working Puppeteer + BotBrowser setup ready for development and production use.
Privacy Impact: Why Puppeteer + BotBrowser
Standard Puppeteer launches a stock Chromium binary that exposes the real host machine's fingerprint. Every browser instance shares the same GPU, fonts, screen resolution, and platform characteristics. Automation indicators like navigator.webdriver are also present, making it clear the browser is being controlled programmatically.
With BotBrowser, each Puppeteer instance can load a different fingerprint profile, giving it a unique browser identity. The fingerprint is applied at the engine level, before any page loads and before any JavaScript executes. Your Puppeteer automation code does not need to change at all. The identity control happens at the launch configuration level.
This makes it possible to run multiple concurrent Puppeteer instances, each with a distinct fingerprint profile, proxy, timezone, and locale, all from the same host machine. The separation is clean: Puppeteer controls what the browser does, BotBrowser controls what the browser is.
Technical Background
Why puppeteer-core, Not puppeteer
The standard puppeteer npm package bundles its own Chromium binary. During installation, it downloads a specific Chromium version managed by the Puppeteer team. This binary does not include BotBrowser's fingerprint control capabilities.
The puppeteer-core package provides the same API without bundled browsers. It requires you to specify an executablePath when launching, which is how you point Puppeteer at the BotBrowser binary.
# Install puppeteer-core, not puppeteer
npm install puppeteer-core
The defaultViewport Setting
Puppeteer has a critical default behavior: it sets a defaultViewport of 800x600 pixels on every new page. This overrides any viewport dimensions defined in the BotBrowser fingerprint profile, causing mismatches between the reported screen size (from the profile) and the actual viewport size (800x600 from Puppeteer).
Always set defaultViewport: null to prevent this override and let the BotBrowser profile control viewport dimensions.
CDP Access
Puppeteer provides direct CDP access through CDPSession objects. This is important for BotBrowser because some advanced features are controlled through custom CDP commands:
// Browser-level CDP session
const cdpSession = await browser.target().createCDPSession();
// BotBrowser CDP commands
await cdpSession.send('BotBrowser.setCustomHeaders', {
headers: { 'X-Custom': 'value' }
});
Note that BotBrowser CDP commands must be sent to the browser-level session, not a page-level session. Using page.createCDPSession() will result in a "method not found" error for BotBrowser-specific commands.
Common Approaches and Their Limitations
Using Puppeteer's Bundled Browser
Puppeteer's bundled Chromium works for basic automation but provides no fingerprint control. Every instance has the same fingerprint, and automation signals are detectable.
Stealth Plugins
The puppeteer-extra-plugin-stealth package patches some automation indicators through JavaScript injection. However, it operates at the wrong level. JavaScript injection happens after page creation and can be detected through timing analysis, prototype chain inspection, and other techniques. It also does not control rendering-level signals like canvas output, WebGL renderer strings, or font availability.
Manual User-Agent Setting
Puppeteer allows setting a custom User-Agent with page.setUserAgent(). This changes one signal but leaves all others (platform, fonts, GPU, screen metrics) at their real values. The inconsistency between the User-Agent and other signals is itself a detectable signal.
BotBrowser's Approach
BotBrowser integrates with Puppeteer through the standard executablePath parameter in puppeteer.launch(). No plugins, patches, or middleware are needed.
Zero-Code Integration
Adding BotBrowser to an existing Puppeteer project requires changing only the launch configuration. All page-level code (selectors, navigation, data extraction) remains exactly the same.
Engine-Level Signals
The fingerprint profile is loaded during browser initialization. By the time Puppeteer connects via CDP, all signals are already in place. There is no race condition, no setup phase, and no risk of the real fingerprint being exposed during startup.
Full CDP Compatibility
BotBrowser extends the standard CDP with custom commands for advanced features like custom headers and per-context configuration. All standard CDP commands continue to work as documented.
Configuration and Usage
Prerequisites
- BotBrowser binary (download from GitHub)
- A fingerprint profile file (
.encformat) - Node.js 18+
npm install puppeteer-core
Ensure the BotBrowser binary has execute permissions:
chmod +x /path/to/botbrowser/chrome
Basic Launch
const puppeteer = require('puppeteer-core');
(async () => {
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
],
headless: true,
defaultViewport: null, // Critical: let profile control viewport
});
const page = await browser.newPage();
await page.goto('https://example.com');
const title = await page.title();
console.log('Page title:', title);
await browser.close();
})();
Profile + Proxy + Locale
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--proxy-server=socks5://user:pass@proxy:1080',
'--bot-config-timezone=Europe/London',
'--bot-config-locale=en-GB',
'--bot-config-languages=en-GB,en',
],
headless: true,
defaultViewport: null,
});
Complete Identity Setup
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--proxy-server=socks5://user:pass@us-proxy:1080',
'--bot-config-timezone=America/New_York',
'--bot-config-locale=en-US',
'--bot-config-languages=en-US,en',
'--bot-inject-random-history',
'--bot-bookmarks=[{"title":"Google","type":"url","url":"https://www.google.com"}]',
'--bot-cookies=@/path/to/cookies.json',
'--bot-always-active',
],
headless: true,
defaultViewport: null,
});
Multiple Identities
async function createSession(profilePath, proxyUrl, timezone) {
const args = [
`--bot-profile=${profilePath}`,
];
if (proxyUrl) args.push(`--proxy-server=${proxyUrl}`);
if (timezone) args.push(`--bot-config-timezone=${timezone}`);
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
args,
headless: true,
defaultViewport: null,
});
return browser;
}
// Launch multiple distinct identities
const sessions = await Promise.all([
createSession('/profiles/win11-us.enc', 'socks5://u:p@us:1080', 'America/New_York'),
createSession('/profiles/win11-de.enc', 'socks5://u:p@de:1080', 'Europe/Berlin'),
createSession('/profiles/android-jp.enc', 'socks5://u:p@jp:1080', 'Asia/Tokyo'),
]);
Network Interception
const page = await browser.newPage();
// Enable request interception
await page.setRequestInterception(true);
page.on('request', request => {
if (request.resourceType() === 'image') {
request.abort();
} else {
request.continue();
}
});
await page.goto('https://example.com');
Screenshot and PDF
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'networkidle0' });
// Screenshot
await page.screenshot({ path: 'screenshot.png', fullPage: true });
// PDF
await page.pdf({ path: 'page.pdf', format: 'A4' });
Headless on Ubuntu
For Linux servers, set the DISPLAY environment variable:
const puppeteer = require('puppeteer-core');
// Set DISPLAY before launching
process.env.DISPLAY = ':10.0';
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
],
headless: true,
defaultViewport: null,
});
Or set it in the shell before running your script:
DISPLAY=:10.0 node script.js
Verification
Verify the integration by checking fingerprint signals:
const page = await browser.newPage();
await page.goto('https://example.com');
const fingerprint = await page.evaluate(() => ({
userAgent: navigator.userAgent,
platform: navigator.platform,
languages: navigator.languages,
hardwareConcurrency: navigator.hardwareConcurrency,
deviceMemory: navigator.deviceMemory,
webdriver: navigator.webdriver,
screenWidth: screen.width,
screenHeight: screen.height,
innerWidth: window.innerWidth,
innerHeight: window.innerHeight,
dpr: devicePixelRatio,
touchPoints: navigator.maxTouchPoints,
}));
console.log('Fingerprint:', JSON.stringify(fingerprint, null, 2));
Best Practices
- Always set
defaultViewport: null. This is the single most important configuration. Without it, Puppeteer overrides the profile's viewport to 800x600. - Use
puppeteer-core, not the fullpuppeteerpackage. The full package downloads an unnecessary Chromium binary. - Use absolute paths for
--bot-profileand other file-based flags. Relative paths may resolve incorrectly. - Close browser instances when done. Each instance is a Chrome process. Failing to close them leaks memory and CPU.
- Set
DISPLAY=:10.0on Linux servers, even for headless mode. - Handle errors gracefully. Wrap launch and navigation in try/catch blocks. Browser launches can fail due to missing dependencies or incorrect paths.
Frequently Asked Questions
Can I use the full puppeteer package instead of puppeteer-core?
Technically yes, but it will download an unnecessary Chromium binary during npm install. Use puppeteer-core to save disk space and make it clear that you are using a custom browser binary.
Do I need puppeteer-extra or stealth plugins?
No. BotBrowser handles fingerprint control at the engine level. Stealth plugins add JavaScript-level patches that are unnecessary with BotBrowser and may interfere with its operation.
Why does my viewport show as 800x600?
You forgot to set defaultViewport: null in the launch options. This is Puppeteer's default behavior. Add defaultViewport: null to let the BotBrowser profile control the viewport.
How do I handle proxy authentication?
BotBrowser extends --proxy-server to accept embedded credentials: --proxy-server=socks5://user:pass@host:port. Do not use Puppeteer's page.authenticate() for proxy auth, as it can disable BotBrowser's automatic geo-detection.
Can I use Puppeteer's browser.createBrowserContext() for per-context proxy?
BotBrowser supports per-context proxy through its own mechanism (ENT Tier1). Using createBrowserContext({ proxyServer }) with Playwright-compatible syntax depends on your Puppeteer version and BotBrowser tier.
How do I debug if the profile is not loading?
Check these common issues:
- The profile path is absolute and the file exists
- The BotBrowser binary has execute permissions
- Use
--bot-internal --v=1flags to enable debug logging - Check the browser's console output for profile loading errors
What Node.js version is required?
Node.js 18 or higher is recommended. Puppeteer-core requires a minimum of Node.js 16.
Can I use TypeScript?
Yes. puppeteer-core includes TypeScript definitions:
import puppeteer, { Browser, Page } from 'puppeteer-core';
Summary
Integrating BotBrowser with Puppeteer requires installing puppeteer-core, pointing executablePath to the BotBrowser binary, adding --bot-profile to launch arguments, and setting defaultViewport: null. All standard Puppeteer features work without modification.
For related topics, see Getting Started with Playwright for the Playwright equivalent, CLI Recipes for flag combinations, Headless Server Setup for production deployment, and Profile Management for organizing profiles.