Puppeteer Extra Plugin Stealth: Is It Still Maintained?
The stealth plugin's last release was March 2023. See what that means, how Puppeteer and scrapy-playwright compare, and how to render a protected page.

Get the rendered HTML of a page that blocks plain headless browsers, without running an evasion stack. Paste this into your agent:
Set up https://monid.ai/SKILL.md, and then use Monid to render https://bot.sannysoft.com/ in a real browser and tell me which checks on the page passed and which failed.
One prompt renders a page in a maintained browser. 1 min setup · 8-18 hrs saved.
You searched for puppeteer-extra-plugin-stealth because you either use it or you are about to, and the question underneath is whether it still holds up. This guide gives the release facts we read from the registries on 2026-10-16, the result of rendering a public bot-test page in a hosted browser, and what to do about the gap between a frozen plugin and a browser that keeps shipping. It runs through Monid, the OpenRouter for agent tools: one key, one balance, every tool priced per call.
What is the easiest way to render a page that detects headless browsers?
Monid. Install it once, add a key, and your agent calls a rendering endpoint with a URL and gets the page back from a browser someone else keeps current, with no evasion plugin of your own to maintain.
- Pay per call. Each render is billed on its own, at $0.002533 per page for HTML (Verified 2026-10-16 on the web extraction tools page). Buy calls as you need them: no monthly plan and no minimum top-up. The endpoint's own notes say failed or blocked requests are not billed. Discovery and inspection are free.
- One install, every tool. The same key and balance also reach search, crawling and the other endpoints on Monid, so a job that starts as "render this page" can grow into "list the site's URLs" without a second vendor.
- Lives inside your agent. Install the CLI, add your key, run the prompt above, and the renderer is there whenever your agent needs a page, with no browser fleet to start or patch.
Is puppeteer-extra-plugin-stealth still maintained, and when was its last release?
The npm registry lists version 2.11.2 as the latest release, published on 2023-03-01. On 2026-10-16 the Puppeteer package itself was at 25.14.0, published two days earlier on 2026-10-14. The plugin whose job is to keep up with how sites detect headless Chromium is three and a half years behind the browser tool it patches.
What the registries say
Read on 2026-10-16 from the npm and PyPI registries:
| Package | Latest | Published |
|---|---|---|
puppeteer | 25.14.0 | 2026-10-14 |
puppeteer-extra | 3.3.6 | 2023-03-01 |
puppeteer-extra-plugin-stealth | 2.11.2 | 2023-03-01 |
rebrowser-puppeteer-core | 24.8.1 | 2025-05-09 |
puppeteer-real-browser | 1.4.4 | 2025-09-03 |
scrapy-playwright | 0.0.48 | 2026-07-10 |
The plugin and its host framework, puppeteer-extra, last shipped within twelve minutes of each other on the same day. The npm page for the plugin says "Published 4 years ago", lists 69 versions and 1,024 dependents, and shows 467,897 weekly downloads. People still install it in large numbers, which is why the release date matters: a lot of projects inherit a snapshot without looking at its age.
What the project says about itself
The README on the npm page has a status line saying puppeteer-extra with the stealth plugin passes all public bot tests. It also describes the work as a cat and mouse game and asks for pull requests when something new comes up. The copyright line reads 2018 to 2023. Read together, that is a project that was actively tracked for a few years and has not published since.
Where the forks stand
rebrowser-puppeteer-core (2025-05-09) and puppeteer-real-browser (2025-09-03) came later than the original, and they exist because people wanted a newer layer on top of Puppeteer. Both are newer than the 2023 release and older than today's Puppeteer. A fork is a different snapshot on a different date; it is worth testing against your own targets rather than assuming it is current.
Does puppeteer-extra-plugin-stealth still work?
For the obvious checks, yes; for anything newer than its last release, nothing in the package can say, because it only patches what it was told about before March 2023. The plugin's README lists the properties it covers, among them navigator.webdriver, the plugins array, the languages array and the chrome object, and public fingerprint pages still test those.
We rendered one of those pages, bot.sannysoft.com, through a hosted browser on 2026-10-16 to see what a maintained browser reports.
The call
monid run -p context.dev -e /web/scrape/html --query '{"url": "https://bot.sannysoft.com/"}'
The endpoint is /web/scrape/html on the provider context.dev. It takes a url and returns the fully rendered HTML plus metadata. We ran it once with maxAgeMs set to 0 so the answer was not a cached copy, and the call completed with HTTP 200 and one billed unit.
What came back
The page's own checks, filled in by the browser that rendered it:
| Check | Result |
|---|---|
| User agent | Chrome/155.0.0.0 on X11 Linux, passed |
| WebDriver | missing (passed) |
| WebDriver Advanced | passed |
| Chrome object | present (passed) |
| Plugins length | 5, PluginArray type passed |
| Languages | en-US,en |
| WebGL vendor | Google Inc. (Google), passed |
| WebGL renderer | ANGLE on SwiftShader, marked failed |
| Broken image dimensions | 16x16, passed |
The one failure is real and worth stating plainly. The renderer string names SwiftShader, which is a software renderer, and the test page flags that. A hosted browser is a maintained browser, not a perfect human, and a site that fingerprints the GPU can see it. Two further blocks on the page, the Fingerprint Scanner table and the Fp-collect output, came back empty in the HTML we received, because they fill in with a script after load and we set no extra wait. The endpoint has a waitForMs option for that case.
A fingerprint is a property of the browser on the day you test it, not a fact about the tool, so rerun the page when it matters.
Is there a Python or Playwright version of the stealth plugin?
Not as the same package. puppeteer-extra-plugin-stealth is a JavaScript plugin for puppeteer-extra, and its README describes it as a plugin for puppeteer-extra and playwright-extra. Python scraping projects usually reach a browser through Playwright, and scrapy-playwright is the common bridge for Scrapy.
What scrapy-playwright does
On PyPI, scrapy-playwright is at version 0.0.48, released 2026-07-10, and requires Python 3.10 or newer. It connects Scrapy's scheduler to a Playwright browser, so a request that sets meta={"playwright": True} is rendered in the browser while the rest of the spider, with its middleware, pipelines and retries, keeps working. It is maintained, which is why it appears here as a contrast rather than a warning.
What it does not do
It is an integration, not an evasion layer. A Playwright browser that Scrapy drives is a Playwright browser with the fingerprint that comes with it. Rendering a page and not being detected are separate problems, and the package solves the first one.
Render only the pages that need it
Browsers cost memory and time, so the pattern that keeps a crawl fast is to fetch list pages over plain HTTP and send only the detail pages that need JavaScript through the browser:
def parse(self, response):
for url in response.css("a.item::attr(href)").getall():
yield response.follow(url, self.parse_item) # plain HTTP
def parse_item(self, response):
if not response.css("div.price"): # only when needed
yield response.request.replace(
meta={"playwright": True}, dont_filter=True)
Where a hosted renderer fits
It can stand in as the downloader for the pages that need a browser. Scrapy stays in charge of scheduling, deduplication and pipelines, and the rendering call goes out per page. The split is to keep the framework and drop the infrastructure underneath it.
What goes wrong when an evasion layer goes stale?
It fails without an error. A site that changes its detection tells you nothing, so a stale plugin shows up as pages that come back shorter, as a challenge, or with the content missing, and your code sees a normal response.
Why does it fail quietly?
An anti-bot vendor can ship a new signal at any time with no changelog, no deprecation notice and no message saying your fingerprint is wrong. A stealth plugin is a snapshot of what detection looked like on its release date. It does not degrade with an exception, it degrades into bad data, and the rate depends on how fast the other side moves.
Why does a status check not catch it?
A challenge page can return HTTP 200, and it can be a few kilobytes of valid HTML. So status == 200 and len(html) > 0 both pass on a block. Assert on a string that only the real page contains:
CANARY = "https://example.com/a-page-you-know"
MUST_CONTAIN = "Some text only the real page has"
def usable(html: str) -> bool:
if len(html) < 2000: # challenge pages are small
return False
if "captcha" in html[:4000].lower():
return False
return MUST_CONTAIN in html # the real check
Run the canary on a schedule and alert on the rate of failures, not on a single one.
What if you only wanted the text?
Many scraping projects reach for a headless browser to get readable content and never needed the DOM. Markdown skips both the browser and the selectors:
monid run -p context.dev -e /web/scrape/markdown --query '{"url": "https://www.npmjs.com/package/puppeteer-extra-plugin-stealth"}'
We ran this on 2026-10-16 against the plugin's npm page. It returned 21,607 characters of Markdown with the title, description, keywords, Open Graph and Twitter fields, and a list of headings. The page's install line, version, dependents count and README all came through as text, one billed unit.
Give this to your agent![]()
Set up https://monid.ai/SKILL.md, and then use Monid to fetch these 40 URLs as markdown, and give me two lists: the ones containing the article body, and the ones that came back as a challenge page.Which endpoint should I use for which job?
| Endpoint | What it does | Input | Billing | Price (Verified 2026-10-16) |
|---|---|---|---|---|
context.dev/web/scrape/html | Renders a page and returns the HTML | url | Per call | $0.002533 |
context.dev/web/scrape/markdown | Renders a page and returns Markdown plus page metadata | url | Per call | $0.002533 |
Prices Verified 2026-10-16 with monid inspect. Both endpoints take the same options: waitForMs for pages that fill in late, country for the proxy exit, useMainContentOnly to drop navigation, and maxAgeMs to control caching. Caching matters for the bill: the default reuses a result younger than one day, so set maxAgeMs to 0 when you want a fresh fetch.
Use HTML when you have your own selectors. Use Markdown when the goal is the text. For many pages of one site, the crawl endpoint returns one document per page. Current prices for every endpoint are on the web extraction tools page.
📖 See also Node Unblocker: what it does not do
When is another route a better fit?
You are automating, not extracting. Filling forms, clicking through a flow, taking screenshots at set viewport sizes or driving a logged-in session. A render endpoint returns a page; it does not operate one. Puppeteer and Playwright are the right tools, and and browser automation without an API covers how to tell which side of that line your job is on.
You are logged in. Cookies and anything behind your own credentials belong in a browser you control.
Volume makes infrastructure cheaper than per-call billing. There is a crossover where running browsers costs less than paying per page. Where it sits depends on your volume and on what an hour of engineering costs you, and it is a legitimate reason to own the stack.
You are testing your own site. Detection is not adversarial there, stealth is irrelevant, and Playwright is the correct tool.
You need a specific GPU fingerprint. The page test above flagged a software renderer. If your target checks the GPU, a browser on hardware you control can present one.
And the disclosure: we resell this endpoint on a blog about it. The software-renderer result above is a real weakness of the endpoint we are recommending, and the cases in this section are cases where you should not buy from us.
Conclusion
The quickest way to get a page from a site that blocks plain headless browsers is one render call through Monid: $0.002533 a page (Verified 2026-10-16), one key for this and every other tool, about a minute to set up, and ready inside your agent when it needs a page. Keeping puppeteer-extra-plugin-stealth in a project that works today is a reasonable call; adopting it new means adopting a 2023 snapshot.
Two things matter more than the choice. Check the release date of any evasion layer before you build on it, with npm view puppeteer-extra-plugin-stealth time.modified against npm view puppeteer time.modified. And add a content-level canary, because a block rarely announces itself.
How we estimated the time saved: building and hosting your own render path with an evasion layer is roughly 4 to 8 hours for the browser setup, the plugin wiring and a first canary, plus 4 to 10 hours in the first month for the fixes when targets change. That is 8 to 18 hours. The 1 minute is the install, key and first call once you already have a Monid key.
Free next step: run monid inspect -p context.dev -e /web/scrape/html, then render one page you know and compare it with what your own browser shows. Start at monid.ai.
FAQ
When was puppeteer-extra-plugin-stealth last updated?
Version 2.11.2 was published on 2023-03-01, per the npm registry on 2026-10-16. puppeteer-extra, the host framework, last shipped on the same day. Puppeteer itself was at 25.14.0, published 2026-10-14.
Should you remove puppeteer-extra-plugin-stealth from an existing project?
Not on the release date alone. If your targets are not using modern detection and your success rate is good, it is doing a job. Add a content canary so a slide in the success rate reaches you as an alert. The dependency is not the risk; the missing signal is.
Are rebrowser-puppeteer-core and puppeteer-real-browser worth switching to?
They are newer than the original: 2025-05-09 and 2025-09-03 on the registry. A newer snapshot is not an ongoing guarantee, so test each against your own targets before you switch, because that is the only comparison that tells you anything.
Is scrapy-playwright better than scrapy-splash?
For new work, yes, and the reason is maintenance. scrapy-playwright is at 0.0.48 (2026-07-10) and drives current Chromium, Firefox or WebKit in the Scrapy process. Splash is a separate rendering service, which is the same pattern as calling a hosted render endpoint, with the browser kept current by someone else.
How many browsers can you run at once?
Far fewer than HTTP requests. Each Chromium context uses hundreds of megabytes, so a machine that handles hundreds of plain requests handles a dozen or so browsers. Set the browser context limit and Scrapy's concurrency to numbers your memory supports, and route only the pages that need rendering through the browser.
Published September 2026.

