Puppeteer Stealth: How to Avoid Bot Detection

If you search for puppeteer stealth, you are usually dealing with one of these problems:

  • headless Chrome gets challenged
  • pages work locally but fail in production
  • one IP gets blocked and the whole job falls over

The mistake is thinking stealth is a single package install.

It is not.

Real stealth is a stack:

  • browser fingerprint hygiene
  • realistic pacing
  • stable session behavior
  • proxy rotation and cooldowns
  • good failure detection
Stealth plugins are not enough on their own

If your Puppeteer jobs are burning IPs or failing unpredictably, the missing piece is usually the network layer. ProxiesAPI helps you rotate and recover without rebuilding the automation code every time.


What stealth plugins actually do

The most common setup is:

npm i puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
import puppeteer from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";

puppeteer.use(StealthPlugin());

That plugin helps by patching some obvious automation signals, such as:

  • navigator.webdriver
  • some headless-specific browser quirks
  • pieces of the default fingerprint that scream "bot"

That is useful.

It is also incomplete.

It does not solve:

  • bad IP reputation
  • aggressive request rates
  • weird timezone and locale mismatches
  • unrealistic user behavior

So yes, stealth plugins matter. No, they are not the whole answer.


Start with a sane baseline browser profile

Before you add clever tricks, fix the obvious issues:

import puppeteer from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";

puppeteer.use(StealthPlugin());

const browser = await puppeteer.launch({
  headless: true,
  args: [
    "--no-sandbox",
    "--disable-setuid-sandbox",
    "--lang=en-US,en",
  ],
});

const page = await browser.newPage();
await page.setViewport({ width: 1366, height: 768 });
await page.setUserAgent(
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " +
  "AppleWebKit/537.36 (KHTML, like Gecko) " +
  "Chrome/125.0.0.0 Safari/537.36"
);
await page.setExtraHTTPHeaders({ "Accept-Language": "en-US,en;q=0.9" });

This does not make you invisible. It just stops you from looking obviously synthetic.


The biggest stealth mistake: rotating everything

Many teams rotate:

  • user agent
  • viewport
  • timezone
  • language

...on every request.

That often looks less human, not more human.

A better model is:

  • keep one coherent profile per session
  • rotate IPs when needed
  • keep timezone, viewport, and locale internally consistent

Example session profile:

function buildSessionProfile(seed = 1) {
  const viewports = [
    { width: 1366, height: 768 },
    { width: 1440, height: 900 },
    { width: 1536, height: 864 },
  ];

  return {
    viewport: viewports[seed % viewports.length],
    locale: "en-US",
    timezoneId: "America/New_York",
  };
}

The goal is not randomness. The goal is plausible consistency.


Behavior matters more than people admit

If your script:

  • lands on a page
  • clicks immediately
  • extracts the DOM
  • exits

...you may pass simple checks but still fail harder targets.

Add small human-like pacing:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function jitter(base, spread = 400) {
  return base + Math.floor(Math.random() * spread);
}

async function humanize(page) {
  await sleep(jitter(700));
  await page.mouse.move(200, 220);
  await sleep(jitter(300));
  await page.evaluate(() => window.scrollBy(0, 500));
  await sleep(jitter(900));
}

You do not need to simulate a full human. You just need to avoid obviously robotic behavior.


Where stealth plugins still fail

This is the part most "puppeteer stealth" posts skip.

You can still get blocked because of:

  • datacenter IPs that are already dirty
  • too many requests from one ASN
  • login flows that expect longer-lived trust signals
  • TLS and transport fingerprints outside the plugin's control

That is why a proxy plan is not optional for many production targets.

Practical proxy rules

  • do not hammer one IP for the whole crawl
  • keep a cooldown list for blocked IPs
  • rotate on real block signals, not every request
  • keep sessions sticky when the target expects continuity

With ProxiesAPI or any equivalent proxy layer, the job of Puppeteer becomes simpler: render, interact, and extract. The network layer handles the blast radius.


Launch Puppeteer through a proxy

const browser = await puppeteer.launch({
  headless: true,
  args: [
    "--no-sandbox",
    "--disable-setuid-sandbox",
    "--proxy-server=http://USERNAME:PASSWORD@HOST:PORT",
  ],
});

Then combine that with:

  • low concurrency per domain
  • backoff on 403, 429, and challenge pages
  • session reuse where it makes sense

That is much closer to real Puppeteer stealth than just installing a plugin and hoping.


Detect failure early

Do not wait until your parser breaks downstream. Detect block pages in-browser:

async function looksBlocked(page) {
  const html = await page.content();
  const text = html.toLowerCase();

  return (
    text.includes("captcha") ||
    text.includes("access denied") ||
    text.includes("unusual traffic") ||
    text.includes("verify you are human")
  );
}

When you hit a block:

  1. save HTML and screenshot
  2. mark the session as burned
  3. rotate proxy or pause the crawl
  4. retry with backoff

That kind of instrumentation saves far more time than endlessly tweaking one more stealth flag.


Should you use headful mode?

Sometimes.

Headful mode can help on specific targets that behave differently under headless execution. But it costs more CPU and memory, and it is not a silver bullet.

Use headful mode when:

  • a target clearly treats headless sessions differently
  • you need to debug flows visually

Do not default to headful mode for everything. Fix pacing, session quality, and proxies first.


A realistic stealth stack for 2026

If I were building a production Puppeteer stack today, I would use:

  • puppeteer-extra-plugin-stealth
  • a stable session profile per worker
  • moderate delays and scroll behavior
  • proxy rotation with cooldowns
  • block-page detection and logging
  • screenshots and HTML dumps on failure

That stack works because it treats stealth as an operating model, not a gimmick.


Final verdict

The real answer to puppeteer stealth is:

Use the stealth plugin, but stop expecting it to do the whole job.

It is a useful first layer. The rest of the win comes from how you run the browser:

  • consistent profiles
  • realistic pacing
  • proxy hygiene
  • good failure handling

That is how you avoid bot detection without burning through your scraper every time a target tightens its defenses.

Stealth plugins are not enough on their own

If your Puppeteer jobs are burning IPs or failing unpredictably, the missing piece is usually the network layer. ProxiesAPI helps you rotate and recover without rebuilding the automation code every time.

Related guides

Puppeteer Stealth: How to Avoid Bot Detection (Without Getting Your IP Burned)
Practical Puppeteer stealth tactics for 2026: fingerprint pitfalls, realistic browsing behavior, retry strategy, and when to use proxies vs headful mode.
seo#puppeteer stealth#puppeteer#headless
Selenium Web Scraping with Python: Complete Guide
A practical Selenium web scraping with Python guide: setup, waits, selectors, anti-bot basics, exporting data, and when Selenium is the wrong tool. Includes comparison tables and a ProxiesAPI-friendly architecture pattern.
guide#python#selenium#web-scraping
Playwright vs Selenium vs Puppeteer: Which Web Scraping Tool Should You Pick in 2026?
A decision framework for 2026: compare Playwright, Selenium, and Puppeteer for web scraping across detection risk, speed, ecosystem, and reliability—with practical stack recommendations and when proxies still matter.
guides#playwright#selenium#puppeteer
Playwright vs Selenium vs Puppeteer: Which Web Scraping Tool to Pick
A practical comparison of Playwright, Selenium, and Puppeteer for modern web scraping, with tradeoffs around reliability, speed, bot resistance, language support, and operating cost.
seo#playwright vs selenium vs puppeteer#playwright#selenium