Puppeteer Stealth is a plugin designed to disguise Puppeteer’s browser footprint so your automation doesn’t scream “I’m a bot!” to websites. It helps cover up typical signs of automation, like navigator.webdriver
or the telltale HeadlessChrome string, making it easier for your scrapers to sneak past detection.
Getting blocked while using Puppeteer? You're definitely not the only one. Websites these days are quick to pick up on automation—especially when you're using the default, out-of-the-box Puppeteer setup. Clues like webdriver
or even just the wrong user agent can tip them off instantly.
Enter Puppeteer Stealth. This handy plugin is part of the puppeteer-extra
toolkit, and it works behind the scenes to mask the signs that usually give your bot away.
In this guide, I’ll take you step by step through getting Puppeteer Stealth up and running. We’ll start with the basics, but also dive into more advanced tricks you can try when stealth mode alone doesn’t cut it.
Why You Can Trust This Guide
The Problem: Standard Puppeteer sticks out like a sore thumb and often gets caught by anti-bot filters, which means blocked requests and scraping failures.
The Fix: Puppeteer Stealth takes care of the most common red flags bots usually trigger, making your automation blend in more naturally with real users.
The Proof: This plugin is tried, tested, and used by a massive community—with more than 450k weekly downloads on npm. Plus, it consistently passes public bot detection tests.
Step 1: Install Puppeteer Extra and the Stealth Plugin
First things first, you need to get the right packages installed. Puppeteer Stealth works with puppeteer-extra
, not just regular Puppeteer.
Run this in your terminal:
npm install puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
Or with Yarn:
yarn add puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
Quick tip: Already have Puppeteer? You’ll still need puppeteer-extra
—it’s a separate package that enables plugin support.
Installation Pitfalls to Watch Out For
- Wrong imports: Make sure you're importing from
'puppeteer-extra'
, not'puppeteer'
. - Node.js issues: Aim for Node.js v18 or newer to avoid compatibility headaches.
- Dependency hiccups: Stealth loads modular evasions as needed, so don’t worry if you don’t see everything up front.
Step 2: Configure Basic Stealth Mode
Let’s get your stealth setup off the ground. This is the simplest setup to get started:
// Import puppeteer-extra (not regular puppeteer!)
const puppeteer = require('puppeteer-extra');
// Add stealth plugin with default settings
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
// Launch browser with stealth enabled
(async () => {
const browser = await puppeteer.launch({
headless: true // Can use 'new' for newer headless mode
});
const page = await browser.newPage();
await page.goto('https://example.com');
// Your scraping logic here
await browser.close();
})();
If You’re Using TypeScript
Same idea, just cleaner syntax and full type support:
import puppeteer from 'puppeteer-extra';
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
puppeteer.use(StealthPlugin());
// Rest of your code with full type support
Heads up: Don’t forget to register the stealth plugin with puppeteer.use()
before launching your browser. That step’s non-negotiable.
Step 3: Test Your Stealth Setup
Before you dive into scraping the site you’re targeting, it’s a smart move to check that your stealth mode is actually doing its job. Here’s how you can test it:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
(async () => {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
// Test on bot detection site
await page.goto('https://bot.sannysoft.com');
await page.screenshot({ path: 'bot-test.png', fullPage: true });
// Check for common detection points
const detectionResults = await page.evaluate(() => {
return {
webdriver: navigator.webdriver,
headless: navigator.headless,
userAgent: navigator.userAgent,
plugins: navigator.plugins.length
};
});
console.log('Detection test results:', detectionResults);
await browser.close();
})();
What You’re Hoping to See
If things are set up right, you should notice:
navigator.webdriver
returnsundefined
- There’s at least one plugin listed (shouldn’t be empty)
- User agent doesn’t say “HeadlessChrome”
- Bot detection pages give you a green light across the board
Step 4: Apply Advanced Evasion Techniques
Still getting caught? Time to level up. These techniques help refine and customize your stealth approach.
Pick and Choose Your Evasions
Instead of enabling every evasion (some can slow things down), you can manually select the ones you need:
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
// Use specific evasions only
const stealth = StealthPlugin();
stealth.enabledEvasions.delete('user-agent-override');
stealth.enabledEvasions.delete('media.codecs');
puppeteer.use(stealth);
Add Your Own Patches
You can patch browser properties yourself for finer control:
// Patch navigator.platform
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'platform', {
get: () => 'MacIntel'
});
});
// Patch hardware concurrency
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'hardwareConcurrency', {
get: () => 8
});
});
// Patch device memory
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'deviceMemory', {
get: () => 16
});
});
Behave Like a Human
Want to fly under the radar? Move like a real person:
// Add random delays between actions
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Simulate human mouse movements
async function humanLikeClick(page, selector) {
const element = await page.$(selector);
const box = await element.boundingBox();
await page.mouse.move(
box.x + box.width / 2,
box.y + box.height / 2,
{ steps: 10 }
);
await delay(100 + Math.random() * 200);
await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
}
// Random scrolling behavior
async function humanScroll(page) {
await page.evaluate(() => {
const scrollHeight = document.body.scrollHeight;
const currentPosition = window.scrollY;
const randomScroll = Math.random() * 300 + 100;
window.scrollBy({
top: randomScroll,
behavior: 'smooth'
});
});
await delay(1000 + Math.random() * 2000);
}
Rotate Proxies
A fixed IP? That’s asking to be blocked. Rotate through proxies to reduce detection:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
async function launchWithProxy(proxyUrl) {
const browser = await puppeteer.launch({
headless: true,
args: [
`--proxy-server=${proxyUrl}`,
'--no-sandbox',
'--disable-setuid-sandbox'
]
});
return browser;
}
// Rotate through proxy list
const proxies = ['proxy1.com:8080', 'proxy2.com:8080'];
let currentProxy = 0;
async function getNextBrowser() {
const proxy = proxies[currentProxy % proxies.length];
currentProxy++;
return launchWithProxy(proxy);
}
Step 5: Implement Alternative Solutions When Stealth Fails
Sometimes stealth isn’t enough—especially when you’re up against tools like Cloudflare. If that’s the case, try these workarounds:
1. Start with Simple HTTP Requests
Often, a regular HTTP request can get the job done with far less fuss:
const axios = require('axios');
// Often faster and less detectable than browser automation
async function lightweightScraping(url) {
try {
const response = await axios.get(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1'
}
});
return response.data;
} catch (error) {
console.error('Request failed:', error.message);
// Fall back to Puppeteer if needed
}
}
2. Try Playwright with Stealth
Playwright has built-in tools to help dodge detection:
const { chromium } = require('playwright-extra');
const stealth = require('puppeteer-extra-plugin-stealth')();
// Playwright can use Puppeteer plugins!
chromium.use(stealth);
async function stealthPlaywright() {
const browser = await chromium.launch({
headless: false,
args: ['--disable-blink-features=AutomationControlled']
});
const context = await browser.newContext({
viewport: { width: 1920, height: 1080 },
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
});
const page = await context.newPage();
// Your scraping logic
}
3. Use a Custom Browser Profile
Run your bot in a persistent user profile to mimic real browsing:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
// Use a real browser profile for better fingerprinting
async function launchWithProfile() {
const browser = await puppeteer.launch({
headless: false,
userDataDir: './chrome-profile',
args: [
'--disable-blink-features=AutomationControlled',
'--exclude-switches=enable-automation',
'--disable-features=IsolateOrigins,site-per-process'
]
});
return browser;
}
4. Use a Web Scraping API
For heavily guarded sites, sometimes it’s best to let the pros handle it:
// Example using a web scraping API
const axios = require('axios');
async function robustScraping(targetUrl) {
const apiUrl = 'https://api.scrapingservice.com/v1/scrape';
const response = await axios.post(apiUrl, {
url: targetUrl,
render_js: true,
premium_proxy: true,
country: 'US'
}, {
headers: {
'API-Key': 'your-api-key'
}
});
return response.data;
}
Common Mistakes to Avoid
Mistake 1: Importing From the Wrong Package
If you’re using puppeteer
instead of puppeteer-extra
, the stealth plugin won’t work. Always import correctly.
Mistake 2: Reusing the Same Fingerprints
Sites can catch on fast if every request looks exactly the same. Switch up user agents, screen sizes, and device properties.
Mistake 3: Ignoring Rate Limits
Even the best stealth setup won’t help if you hammer a site with hundreds of requests per minute. Slow things down.
Final Thoughts
Puppeteer Stealth is a solid line of defense against basic bot detection, but it’s not a one-size-fits-all solution. The best scraping strategies start simple, scale gradually, and stay adaptive. Here’s the takeaway:
- Try HTTP requests before jumping to a browser
- Use automation when necessary, but not before
- Layer your evasion tactics for best results
- Follow ethical scraping practices
- When it’s too much work—consider using an API
Scraping gets tougher all the time, so the more flexible you are, the better your chances of staying ahead.