Back to Blog
Getting Started

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 (.enc format)
  • 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));
Puppeteer + BotBrowser Setup puppeteer-core Automation API No bundled browser BotBrowser Custom Chromium binary Profile-based identity CDP defaultViewport: null + --bot-profile = consistent identity Same Puppeteer API. Different browser identity per instance.

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 full puppeteer package. The full package downloads an unnecessary Chromium binary.
  • Use absolute paths for --bot-profile and 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.0 on 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:

  1. The profile path is absolute and the file exists
  2. The BotBrowser binary has execute permissions
  3. Use --bot-internal --v=1 flags to enable debug logging
  4. 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.

#puppeteer#automation#getting-started#tutorial