super-puppeteer
v1.0.0
Published
Plain puppeteer patched from scratch (no puppeteer-extra/stealth) to pass pixelscan, rebrowser bot-detector, CreepJS, FingerprintJS and webscraper bot checks.
Maintainers
Readme
super-puppeteer
Plain puppeteer, patched by hand to get past modern bot checks. No puppeteer-extra, no stealth-plugin (that stuff is unmaintained and fingerprintable). Every patch here exists because a real detector actually flagged something, and each one was verified against the live site before it stayed in.
What it clears right now:
| target | result | |---|---| | pixelscan.net/bot-check | "You're Definitely a Human", nothing detected | | bot-detector.rebrowser.net | every leak test green | | creepjs | headless 0%, stealth 0% | | demo.fingerprint.com/playground | bot: not_detected | | webscraper.io/bot-check | 12/12 | | tls.peet.ws | genuine Chrome JA3/JA4 |
install
npm install super-puppeteer puppeteerpuppeteer is a peer dep. Works with the bundled Chrome-for-Testing, but real Chrome (channel: 'chrome') is better.
usage
import { launch } from 'super-puppeteer'
const { browser, page } = await launch({ headless: false })
await page.goto('https://example.com')
await browser.close()Drive a click like a human when the page is watching mouse behavior:
import { launch, humanClick } from 'super-puppeteer'
const { browser, page } = await launch({ headless: false })
await page.goto('https://webscraper.io/bot-check')
await humanClick(page, '#click-me-box')Patch a browser you already run:
import { connect, applyStealth } from 'super-puppeteer'
const browser = await connect(existing.wsEndpoint())
const page = await browser.newPage()
await applyStealth(page)options
await launch({
headless: false,
proxy: 'socks5://host:port',
forceQuicOn: ['webscraper.io:443'],
stealth: {
webrtc: true,
extraEvasions: [],
},
puppeteer: {
channel: 'chrome',
userDataDir: './profile',
},
})api
launch(opts)launch, patch, return{ browser, page }connect(wsEndpoint)attach the patched transport to a running browserapplyStealth(page, opts)install the evasions on a pagehumanClick(page, selector)move the cursor on a real curve and click, searches every frame including cross-origin iframeshumanMove(page, x, y)/humanIdle(page, moves)the movement pieces on their ownStealthTransport,DEFAULT_EVASIONS, and the individual evasion modules for building your own set
how it holds up
Two things do most of the work. The rest are small, targeted surface fixes.
The transport sits on the CDP websocket and strips the pptr: sourceURL that puppeteer stamps onto every script it injects. That marker leaks into stack traces and is exactly what rebrowser's sourceUrlLeak greps for. Killing it on the wire means no editing node_modules.
Launch flags carry the network and GPU story. --disable-blink-features=AutomationControlled makes navigator.webdriver return false natively instead of us faking it. GPU flags plus a one-time warmup keep the hardware GPU hot so an early getContext('webgl', {failIfMajorPerformanceCaveat:true}) answers for real instead of falling back to SwiftShader. And because it's real Chrome making the connection, the JA3/JA4 is a genuine Chrome fingerprint with no uTLS involved.
The page evasions run at document-start and cover every frame:
- webdriver leaves the native
falsegetter alone and only steps in if it's actually true. Deleting the property is what most people do and it's wrong, creepjs flags a missingnavigator.webdriver. - console swallows every method so a logged bait object never gets serialized over CDP, which is how pixelscan's isDevtoolOpen fires. Each replacement reports
[native code]through its own toString, and we never touchFunction.prototype.toStringbecause creepjs uses that for its own lie-detector and will both flag the proxy and poison the webdriver check. - chrome-runtime, plugins and permissions rebuild the surfaces headless drops, with the right prototypes and no-prototype methods so the shape matches real Chrome.
At launch we also repopulate navigator.userAgentData.brands, which puppeteer ships empty. That one blank field is a tell across three different checkers, so filling it consistently with the UA clears all of them at once.
The virtual mouse is real behavior, not a spoof. humanClick walks a bezier path with easing, shrinking jitter and variable pauses, then presses with a human dwell. The events are trusted and Chrome computes real screenX/screenY from the window position, which is why the cross-domain-iframe screen-coordinate check passes without lying about anything.
a few things worth knowing
Don't delete navigator.webdriver, override it only when it's actually true. Never proxy Function.prototype.toString. Don't override WebGL on a real GPU, fix the cold start instead. Less tampering means higher trust, especially on creepjs where every override is a potential lie. And real Chrome hands you a clean JA3/JA4 for free, so keep it that way with SOCKS5 proxies rather than anything that terminates TLS.
Two residuals that aren't bot signals: creepjs likeHeadless sits around 19% from APIs that are genuinely absent on every desktop Chrome, and HTTP/3 only kicks in after the first visit unless you use forceQuicOn.
test
npm testRuns the whole set headful. A real Chrome window opens.
