Puppeteer Stealth + Mobile Proxies: Undetectable Browser Automation
Puppeteer alone leaks via navigator.webdriver, a headless UA, missing plugins, and roughly 15 other signals. The stealth plugin patches those. Combined with mobile carrier IPs, you get traffic that passes both browser fingerprinting AND IP detection.
1. What puppeteer-extra-plugin-stealth actually does
The stealth plugin is a collection of small evasion modules, each targeting one specific headless Chromium tell. They're injected via page.evaluateOnNewDocument before any site JavaScript runs. These are the modules that ship with the default StealthPlugin():
user-agent-overrideRewrites the HeadlessChrome UA to a real Chrome UA and matches Accept-Language + navigator.userAgent + platform.navigator.webdriverHides the navigator.webdriver flag via an ES6 Proxy so property descriptor checks (Object.getOwnPropertyDescriptor) still pass.navigator.pluginsEmulates the classic Chrome plugin triplet: Chrome PDF Plugin, Chrome PDF Viewer, Native Client.navigator.languagesSets a proper languages array (e.g. ['en-US', 'en']) — empty array is a headless tell.navigator.permissionsPatches the notifications query mismatch (denied vs. default) that exposes headless Chrome.chrome.app / chrome.runtime / chrome.csi / chrome.loadTimesMocks the window.chrome object with app, runtime, csi() and loadTimes() that real Chrome exposes.iframe.contentWindowProxies the iframe's contentWindow so cross-frame checks don't reveal the headless runtime.media.codecsSpoofs H.264 / AAC support in HTMLMediaElement.canPlayType(), which headless Chromium reports as 'probably' less often.webgl.vendorRewrites the WebGL UNMASKED_VENDOR / UNMASKED_RENDERER so they no longer leak 'Google SwiftShader'.window.outerdimensionsFixes window.outerWidth / outerHeight being 0 in headless mode.defaultArgsStrips the --enable-automation flag Chromium adds when launched by DevTools, which removes the 'Chrome is being controlled by automated test software' infobar leak.Source: berstend/puppeteer-extra — evasions/. You can toggle individual modules with stealth.enabledEvasions.delete('name').
2. Installation and setup
npm install puppeteer puppeteer-extra puppeteer-extra-plugin-stealth
If you prefer the smaller runtime, swap puppeteer for puppeteer-core and point it at a local Chrome binary with executablePath. The stealth plugin works identically with either.
3. Basic code with a mobile proxy
Chromium can't read the user:pass inline in the --proxy-server flag — you authenticate through page.authenticate(), which responds to the HTTP 407 challenge transparently.
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
(async () => {
const browser = await puppeteer.launch({
headless: 'new',
args: [
'--proxy-server=http://hostname:http_port',
'--no-sandbox',
],
});
const page = await browser.newPage();
await page.authenticate({
username: 'USER',
password: 'PASS',
});
await page.goto('https://bot.sannysoft.com/', {
waitUntil: 'networkidle2',
});
await page.screenshot({ path: 'stealth-test.png', fullPage: true });
await browser.close();
})();That's the whole integration — puppeteer.use(StealthPlugin()) before launch, --proxy-server in args, and page.authenticate for credentials.
4. Testing your stealth setup
Point your stealthy browser at these public fingerprint test pages and inspect the screenshot or the network tab:
- →bot.sannysoft.com — the canonical stealth test. Every row should be green.
- →abrahamjuliot.github.io/creepjs — CreepJS, the most invasive browser fingerprint audit you can run. Aim for a trust score above 60%.
- →browserleaks.com — WebRTC, canvas, WebGL, and DNS leak probes.
- →pixelscan.net — cross-checks fingerprint consistency (UA vs. timezone vs. geo of the exit IP).
Run the same tests without the proxy to see the difference the carrier IP makes on the network-level checks (ASN, geo, reputation).
5. Combining stealth with IP rotation
Rotate between independent sessions, not mid-page. Tear the browser down, hit the rotate endpoint, wait for the modem to reconnect, relaunch:
const API_BASE = 'https://buy.mobileproxies.org';
async function rotateIP(slotId, apiKey) {
const res = await fetch(
`${API_BASE}/api/v1/proxies/${slotId}/switch`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${apiKey}` },
}
);
if (!res.ok) {
throw new Error(`rotate failed: ${res.status}`);
}
// Modem takes ~10s to reconnect and pick up a new carrier IP
await new Promise(r => setTimeout(r, 10000));
}
async function listProxies(apiKey) {
const res = await fetch(
`${API_BASE}/api/v1/proxies`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
);
if (!res.ok) throw new Error(`list failed: ${res.status}`);
return res.json();
}The endpoints use Bearer authentication and match the Swagger at buy.mobileproxies.org/api/docs. Rotate per account, per campaign, or per scrape target — whatever your isolation unit is.
6. What stealth CANNOT do
It won't fix bad IP reputation.
If your exit IP is a flagged AWS range, Cloudflare challenges you before a single byte of JavaScript runs. No amount of fingerprint patching helps. That's the job of the mobile proxy.
It won't beat behavioral analysis.
DataDome, PerimeterX (HUMAN), and Akamai Bot Manager watch mouse velocity, scroll inertia, keystroke dwell/flight times, and request timing entropy. Stealth doesn't move your mouse — you still need humanized interaction scripts.
It won't solve CAPTCHAs.
Cloudflare Turnstile, hCaptcha, and reCAPTCHA v3 score you independently and gate a human challenge. Integrate a dedicated solver (2Captcha, CapSolver, etc.) if you need to pass them.
7. Production checklist
Stealth + real carrier IPs
The browser fingerprint is yours to patch. The IP is ours. Test a mobile slot with programmable rotation for $5.
Related Articles
Technical Deep-Dive
How Websites Detect Proxies in 2026
IP reputation, JA4 TLS, TCP stack, ASN flagging
Developer Guide
Puppeteer Proxy Setup (Node.js)
Proxy auth, API integration, IP rotation mid-session
Multi-Account Management
Multi-Account Management Guide
Browser fingerprinting, isolation, platform strategies