{"_id":"@divriots/puppeteer-extra-plugin-stealth","_rev":"6-b250bc2bc8e8099a6fbed66238fb8736","name":"@divriots/puppeteer-extra-plugin-stealth","dist-tags":{"latest":"2.11.2-1"},"versions":{"2.11.2-1":{"name":"@divriots/puppeteer-extra-plugin-stealth","version":"2.11.2-1","keywords":["puppeteer","puppeteer-extra","puppeteer-extra-plugin","stealth","stealth-mode","detection-evasion","crawler","chrome","headless","pupeteer"],"author":{"name":"berstend"},"license":"MIT","_id":"@divriots/puppeteer-extra-plugin-stealth@2.11.2-1","maintainers":[{"name":"muryoh","email":"muryoh@gmail.com"},{"name":"gqio","email":"quinio.gregory@gmail.com"},{"name":"georges-gomes","email":"georges.gomes@gmail.com"},{"name":"gluck","email":"francois.valdy@gmail.com"},{"name":"_divriots_","email":"admin+npm@divriots.com"}],"homepage":"https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth#readme","bugs":{"url":"https://github.com/berstend/puppeteer-extra/issues"},"ava":{"files":["!test/util.js","!test/fixtures/sw.js"]},"dist":{"shasum":"5d0335e48a41c0ae81d445de7d00ae4adf30d627","tarball":"https://registry.npmjs.org/@divriots/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2-1.tgz","fileCount":99,"integrity":"sha512-iAExeNQB76+VzMAMOrCXkjFovpl+JIw6grb4GF7EeogLc6tuWllLgClcLujX1yL+6BswBqSGTi/3jJ7nHRAsCA==","signatures":[{"sig":"MEUCIGghZRREMEHRzVrxcdyZ3M9bPTLY3IUkxbotr+cPUiBgAiEA6LroWE6+j2poDXE+jJTmPd/iNe3Hv0tswmj+3OIZW1Q=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":273844},"main":"index.js","engines":{"node":">=8"},"gitHead":"babb041828cab50c525e0b9aab02d58f73416ef3","scripts":{"docs":"run-s docs-for-plugin postdocs-for-plugin docs-for-evasions postdocs-for-evasions types","lint":"eslint --ext .js .","test":"run-p test:js","types":"npx --package typescript@3.7 tsc --emitDeclarationOnly --declaration --allowJs index.js","test-ci":"run-s test:js","test:js":"ava --concurrency 2 -v","docs-for-plugin":"documentation readme --quiet --shallow --github --markdown-theme transitivebs --readme-file readme.md --section API index.js","docs-for-evasions":"cd ./evasions && loop \"documentation readme --quiet --shallow --github --markdown-theme transitivebs --readme-file readme.md --section API index.js\"","postdocs-for-plugin":"npx prettier --write readme.md","postdocs-for-evasions":"cd ./evasions && loop \"npx prettier --write readme.md\""},"typings":"index.d.ts","_npmUser":{"name":"muryoh","email":"muryoh@gmail.com"},"repository":{"url":"git+https://github.com/berstend/puppeteer-extra.git","type":"git"},"_npmVersion":"10.8.1","description":"Stealth mode: Applies various techniques to make detection of headless puppeteer harder.","directories":{},"_nodeVersion":"20.16.0","dependencies":{"debug":"^4.1.1","puppeteer-extra-plugin":"^3.2.3","puppeteer-extra-plugin-user-preferences":"^2.4.1"},"_hasShrinkwrap":false,"devDependencies":{"ava":"2.4.0","loop":"^3.0.6","fpcollect":"^1.0.4","fpscanner":"^0.1.5","puppeteer":"9","npm-run-all":"^4.1.5","documentation-markdown-themes":"^12.1.5"},"peerDependencies":{"puppeteer-extra":"*","playwright-extra":"*"},"peerDependenciesMeta":{"puppeteer-extra":{"optional":true},"playwright-extra":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/puppeteer-extra-plugin-stealth_2.11.2-1_1729002907018_0.4102260921764933","host":"s3://npm-registry-packages"}}},"time":{"created":"2024-10-15T14:35:06.900Z","modified":"2025-09-03T07:23:50.224Z","2.11.2-1":"2024-10-15T14:35:07.223Z"},"bugs":{"url":"https://github.com/berstend/puppeteer-extra/issues"},"author":{"name":"berstend"},"license":"MIT","homepage":"https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth#readme","keywords":["puppeteer","puppeteer-extra","puppeteer-extra-plugin","stealth","stealth-mode","detection-evasion","crawler","chrome","headless","pupeteer"],"repository":{"url":"git+https://github.com/berstend/puppeteer-extra.git","type":"git"},"description":"Stealth mode: Applies various techniques to make detection of headless puppeteer harder.","maintainers":[{"email":"quinio.gregory@gmail.com","name":"gqio"},{"email":"francois.valdy@gmail.com","name":"gluck"},{"email":"georges.gomes@gmail.com","name":"georges-gomes"},{"email":"muryoh@gmail.com","name":"muryoh"},{"email":"admin+npm@divriots.com","name":"_divriots_"}],"readme":"# puppeteer-extra-plugin-stealth [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/berstend/puppeteer-extra/test.yml?branch=master&event=push) [![Discord](https://img.shields.io/discord/737009125862408274)](https://extra.community) [![npm](https://img.shields.io/npm/v/puppeteer-extra-plugin-stealth.svg)](https://www.npmjs.com/package/puppeteer-extra-plugin-stealth)\n\n> A plugin for [puppeteer-extra](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra) and [playwright-extra](https://github.com/berstend/puppeteer-extra/tree/master/packages/playwright-extra) to prevent detection.\n\n<p align=\"center\"><img src=\"https://i.imgur.com/q2xBjqH.png\" /></p>\n\n## Install\n\n```bash\nyarn add puppeteer-extra-plugin-stealth\n# - or -\nnpm install puppeteer-extra-plugin-stealth\n```\n\nIf this is your first [puppeteer-extra](https://github.com/berstend/puppeteer-extra) plugin here's everything you need:\n\n```bash\nyarn add puppeteer puppeteer-extra puppeteer-extra-plugin-stealth\n# - or -\nnpm install puppeteer puppeteer-extra puppeteer-extra-plugin-stealth\n```\n\n## Usage\n\n```js\n// puppeteer-extra is a drop-in replacement for puppeteer,\n// it augments the installed puppeteer with plugin functionality\nconst puppeteer = require('puppeteer-extra')\n\n// add stealth plugin and use defaults (all evasion techniques)\nconst StealthPlugin = require('puppeteer-extra-plugin-stealth')\npuppeteer.use(StealthPlugin())\n\n// puppeteer usage as normal\npuppeteer.launch({ headless: true }).then(async browser => {\n  console.log('Running tests..')\n  const page = await browser.newPage()\n  await page.goto('https://bot.sannysoft.com')\n  await page.waitForTimeout(5000)\n  await page.screenshot({ path: 'testresult.png', fullPage: true })\n  await browser.close()\n  console.log(`All done, check the screenshot. ✨`)\n})\n```\n\n<details>\n <summary><strong>TypeScript usage</strong></summary><br/>\n\n> `puppeteer-extra` and most plugins are written in TS,\n> so you get perfect type support out of the box. :)\n\n```ts\nimport puppeteer from 'puppeteer-extra'\nimport StealthPlugin from 'puppeteer-extra-plugin-stealth'\n\npuppeteer\n  .use(StealthPlugin())\n  .launch({ headless: true })\n  .then(async browser => {\n    const page = await browser.newPage()\n    await page.goto('https://bot.sannysoft.com')\n    await page.waitForTimeout(5000)\n    await page.screenshot({ path: 'stealth.png', fullPage: true })\n    await browser.close()\n  })\n```\n\n> Please check this [wiki](https://github.com/berstend/puppeteer-extra/wiki/TypeScript-usage) entry in case you have TypeScript related import issues.\n\n</details><br>\n\n> Please check out the [main documentation](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra) to learn more about `puppeteer-extra` (Firefox usage, other Plugins, etc).\n\n## Status\n\n- ✅ **`puppeteer-extra` with stealth passes all public bot tests.**\n\nPlease note: I consider this a friendly competition in a rather interesting cat and mouse game. If the other team (👋) wants to detect headless chromium there are still ways to do that (at least I noticed a few, which I'll tackle in future updates).\n\nIt's probably impossible to prevent all ways to detect headless chromium, but it should be possible to make it so difficult that it becomes cost-prohibitive or triggers too many false-positives to be feasible.\n\nIf something new comes up or you experience a problem, please do your homework and create a PR in a respectful way (this is Github, not reddit) or I might not be motivated to help. :)\n\n## Changelog\n\n> 🎁 **Note:** Until we've automated changelog updates in markdown files please follow the `#announcements` channel in our [discord server](https://discord.gg/vz7PeKk) for the latest updates and changelog info.\n\n_Older changelog:_\n\n#### `v2.4.7`\n\n- New: `user-agent-override` - Used to set a stealthy UA string, language & platform. This also fixes issues with the prior method of setting the `Accept-Language` header through request interception ([#104](https://github.com/berstend/puppeteer-extra/pull/104), kudos to [@Niek](https://github.com/Niek))\n- New: `navigator.vendor` - Makes it possible to optionally override navigator.vendor ([#110](https://github.com/berstend/puppeteer-extra/pull/110), thanks [@Niek](https://github.com/Niek))\n- Improved: `navigator.webdriver`: Now uses ES6 Proxies to pass `instanceof` tests ([#117](https://github.com/berstend/puppeteer-extra/pull/117), thanks [@aabbccsmith](https://github.com/aabbccsmith))\n- Removed: `user-agent`, `accept-language` (now obsolete)\n\n#### `v2.4.2` / `v2.4.1`\n\n- Improved: `iframe.contentWindow` - We now proxy the original window object and smartly redirect calls that might reveal it's true identity, as opposed to mocking it like peasants :)\n- Improved: `accept-language` - More robust and it's now possible to [set a custom locale](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth/evasions/accept-language#readme) if needed.\n- ⭐️ Passes the [headless-cat-n-mouse](https://github.com/paulirish/headless-cat-n-mouse) test\n\n#### `v2.4.0`\n\nLet's ring the bell for round 2 in this cat and mouse fight 😄\n\n- New: All evasions now have a specific before and after test to make make this whole topic less voodoo\n- New: `media.codecs` - we spoof the presence of proprietary codecs in Chromium now\n- New & improved: `iframe.contentWindow` - Found a way to fix `srcdoc` frame based detection without breaking recaptcha inline popup & other iframes (please report any issues)\n- New: `accept-language` - Adds a missing `Accept-Language` header in headless (capitalized correctly, `page.setExtraHTTPHeaders` is all lowercase which can be detected)\n- Improved: `chrome.runtime` - More extensive mocking of the chrome object\n- ⭐️ All [fpscanner](https://antoinevastel.com/bots/) tests are now green, as well as all [intoli](https://bot.sannysoft.com) tests and the [`areyouheadless`](https://arh.antoinevastel.com/bots/areyouheadless) test\n\n<details>\n <summary><code>v2.1.2</code></summary><br/>\n\n- Improved: `navigator.plugins` - we fully emulate plugins/mimetypes in headless now 🎉\n- New: `webgl.vendor` - is otherwise set to \"Google\" in headless\n- New: `window.outerdimensions` - fix missing window.outerWidth/outerHeight and viewport\n- Fixed: `navigator.webdriver` now returns undefined instead of false\n\n</details>\n\n## Test results (red is bad)\n\n#### Vanilla puppeteer <strong>without stealth 😢</strong>\n\n<table class=\"image\">\n<tr>\n\n  <td><figure class=\"image\"><a href=\"./stealthtests/_results/headless-chromium-vanilla.js.png\"><img src=\"./stealthtests/_results/_thumbs/headless-chromium-vanilla.js.png\"></a><figcaption>Chromium + headless</figcaption></figure></td>\n  <td><figure class=\"image\"><a href=\"./stealthtests/_results/headful-chromium-vanilla.js.png\"><img src=\"./stealthtests/_results/_thumbs/headful-chromium-vanilla.js.png\"></a><figcaption>Chromium + headful</figcaption></figure></td>\n  <td><figure class=\"image\"><a href=\"./stealthtests/_results/headless-chrome-vanilla.js.png\"><img src=\"./stealthtests/_results/_thumbs/headless-chrome-vanilla.js.png\"></a><figcaption>Chrome + headless</figcaption></figure></td>\n  <td><figure class=\"image\"><a href=\"./stealthtests/_results/headful-chrome-vanilla.js.png\"><img src=\"./stealthtests/_results/_thumbs/headful-chrome-vanilla.js.png\"></a><figcaption>Chrome + headful</figcaption></figure></td>\n\n</tr>\n</table>\n\n#### Puppeteer <strong>with stealth plugin 💯</strong>\n\n<table class=\"image\">\n<tr>\n\n  <td><figure class=\"image\"><a href=\"./stealthtests/_results/headless-chromium-stealth.js.png\"><img src=\"./stealthtests/_results/_thumbs/headless-chromium-stealth.js.png\"></a><figcaption>Chromium + headless</figcaption></figure></td>\n  <td><figure class=\"image\"><a href=\"./stealthtests/_results/headful-chromium-stealth.js.png\"><img src=\"./stealthtests/_results/_thumbs/headful-chromium-stealth.js.png\"></a><figcaption>Chromium + headful</figcaption></figure></td>\n  <td><figure class=\"image\"><a href=\"./stealthtests/_results/headless-chrome-stealth.js.png\"><img src=\"./stealthtests/_results/_thumbs/headless-chrome-stealth.js.png\"></a><figcaption>Chrome + headless</figcaption></figure></td>\n  <td><figure class=\"image\"><a href=\"./stealthtests/_results/headful-chrome-stealth.js.png\"><img src=\"./stealthtests/_results/_thumbs/headful-chrome-stealth.js.png\"></a><figcaption>Chrome + headful</figcaption></figure></td>\n\n</tr>\n</table>\n\n> Note: The `MQ_SCREEN` test is broken on their page (will fail in regular Chrome as well).\n\nTests have been done using [this test site](https://bot.sannysoft.com/) and [these scripts](./stealthtests/).\n\n#### Improved reCAPTCHA v3 scores\n\nUsing stealth also seems to help with maintaining a normal [reCAPTCHA v3 score](https://developers.google.com/recaptcha/docs/v3#score).\n\n<table class=\"image\">\n<tr>\n\n  <td><figure class=\"image\"><figcaption><code>Regular Puppeteer</code></figcaption><br/><img src=\"https://i.imgur.com/rHEH69b.png\"></figure></td>\n  <td><figure class=\"image\"><figcaption><code>Stealth Puppeteer</code></figcaption><br/><img src=\"https://i.imgur.com/2if496Z.png\"></figure></td>\n\n</tr>\n</table>\n\nNote: The [official test](https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php) is to be taken with a grain of salt, as the score is calculated individually per site and multiple other factors (past behaviour, IP address, etc). Based on anecdotal observations it still seems to work as a rough indicator.\n\n_**Tip:** Have a look at the [recaptcha plugin](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-recaptcha) if you have issues with reCAPTCHAs._\n\n## API\n\n<!-- Generated by documentation.js. Update this documentation by updating the source code. -->\n\n#### Table of Contents\n\n- [puppeteer-extra-plugin-stealth \\[ ](#puppeteer-extra-plugin-stealth---)\n  - [Install](#install)\n  - [Usage](#usage)\n  - [Status](#status)\n  - [Changelog](#changelog)\n    - [`v2.4.7`](#v247)\n    - [`v2.4.2` / `v2.4.1`](#v242--v241)\n    - [`v2.4.0`](#v240)\n  - [Test results (red is bad)](#test-results-red-is-bad)\n    - [Vanilla puppeteer without stealth 😢](#vanilla-puppeteer-without-stealth-)\n    - [Puppeteer with stealth plugin 💯](#puppeteer-with-stealth-plugin-)\n    - [Improved reCAPTCHA v3 scores](#improved-recaptcha-v3-scores)\n  - [API](#api)\n    - [Table of Contents](#table-of-contents)\n    - [class: StealthPlugin](#class-stealthplugin)\n      - [Purpose](#purpose)\n      - [Modularity](#modularity)\n      - [Contributing](#contributing)\n      - [Kudos](#kudos)\n      - [.availableEvasions](#availableevasions)\n      - [.enabledEvasions](#enabledevasions)\n    - [defaultExport(opts?)](#defaultexportopts)\n  - [License](#license)\n\n### class: [StealthPlugin](https://github.com/berstend/puppeteer-extra/blob/e6133619b051febed630ada35241664eba59b9fa/packages/puppeteer-extra-plugin-stealth/index.js#L72-L162)\n\n- `opts` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** Options (optional, default `{}`)\n  - `opts.enabledEvasions` **[Set](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>?** Specify which evasions to use (by default all)\n\n**Extends: PuppeteerExtraPlugin**\n\nStealth mode: Applies various techniques to make detection of headless puppeteer harder. 💯\n\n#### Purpose\n\nThere are a couple of ways the use of puppeteer can easily be detected by a target website.\nThe addition of `HeadlessChrome` to the user-agent being only the most obvious one.\n\nThe goal of this plugin is to be the definite companion to puppeteer to avoid\ndetection, applying new techniques as they surface.\n\nAs this cat & mouse game is in it's infancy and fast-paced the plugin\nis kept as flexibile as possible, to support quick testing and iterations.\n\n#### Modularity\n\nThis plugin uses `puppeteer-extra`'s dependency system to only require\ncode mods for evasions that have been enabled, to keep things modular and efficient.\n\nThe `stealth` plugin is a convenience wrapper that requires multiple [evasion techniques](./evasions/)\nautomatically and comes with defaults. You could also bypass the main module and require\nspecific evasion plugins yourself, if you whish to do so (as they're standalone `puppeteer-extra` plugins):\n\n```es6\n// bypass main module and require a specific stealth plugin directly:\npuppeteer.use(\n  require('puppeteer-extra-plugin-stealth/evasions/console.debug')()\n)\n```\n\n#### Contributing\n\nPRs are welcome, if you want to add a new evasion technique I suggest you\nlook at the [template](./evasions/_template) to kickstart things.\n\n#### Kudos\n\nThanks to [Evan Sangaline](https://intoli.com/blog/not-possible-to-block-chrome-headless/) and [Paul Irish](https://github.com/paulirish/headless-cat-n-mouse) for kickstarting the discussion!\n\n---\n\nExample:\n\n```javascript\nconst puppeteer = require('puppeteer-extra')\n// Enable stealth plugin with all evasions\npuppeteer.use(require('puppeteer-extra-plugin-stealth')())\n;(async () => {\n  // Launch the browser in headless mode and set up a page.\n  const browser = await puppeteer.launch({\n    args: ['--no-sandbox'],\n    headless: true\n  })\n  const page = await browser.newPage()\n\n  // Navigate to the page that will perform the tests.\n  const testUrl =\n    'https://intoli.com/blog/' +\n    'not-possible-to-block-chrome-headless/chrome-headless-test.html'\n  await page.goto(testUrl)\n\n  // Save a screenshot of the results.\n  const screenshotPath = '/tmp/headless-test-result.png'\n  await page.screenshot({ path: screenshotPath })\n  console.log('have a look at the screenshot:', screenshotPath)\n\n  await browser.close()\n})()\n```\n\n---\n\n#### .[availableEvasions](https://github.com/berstend/puppeteer-extra/blob/e6133619b051febed630ada35241664eba59b9fa/packages/puppeteer-extra-plugin-stealth/index.js#L128-L130)\n\nType: **[Set](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**\n\nGet all available evasions.\n\nPlease look into the [evasions directory](./evasions/) for an up to date list.\n\nExample:\n\n```javascript\nconst pluginStealth = require('puppeteer-extra-plugin-stealth')()\nconsole.log(pluginStealth.availableEvasions) // => Set { 'user-agent', 'console.debug' }\npuppeteer.use(pluginStealth)\n```\n\n---\n\n#### .[enabledEvasions](https://github.com/berstend/puppeteer-extra/blob/e6133619b051febed630ada35241664eba59b9fa/packages/puppeteer-extra-plugin-stealth/index.js#L145-L147)\n\nType: **[Set](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>**\n\nGet all enabled evasions.\n\nEnabled evasions can be configured either through `opts` or by modifying this property.\n\nExample:\n\n```javascript\n// Remove specific evasion from enabled ones dynamically\nconst pluginStealth = require('puppeteer-extra-plugin-stealth')()\npluginStealth.enabledEvasions.delete('console.debug')\npuppeteer.use(pluginStealth)\n```\n\n---\n\n### [defaultExport(opts?)](https://github.com/berstend/puppeteer-extra/blob/e6133619b051febed630ada35241664eba59b9fa/packages/puppeteer-extra-plugin-stealth/index.js#L170-L170)\n\n- `opts` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?** Options\n  - `opts.enabledEvasions` **[Set](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set)&lt;[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)>?** Specify which evasions to use (by default all)\n\nDefault export, PuppeteerExtraStealthPlugin\n\n---\n\n## License\n\nCopyright © 2018 - 2023, [berstend̡̲̫̹̠̖͚͓̔̄̓̐̄͛̀͘](mailto:github@berstend.com?subject=[GitHub]%20PuppeteerExtra). Released under the MIT License.\n","readmeFilename":"readme.md"}