Integrations

Puppeteer Stealth + CaptchaAI: Reliable Browser Automation

Standard Puppeteer gets detected immediately by anti-bot systems. puppeteer-extra-plugin-stealth patches the telltale signs — navigator properties, WebGL, Chrome runtime flags — that reveal automation. Combined with CaptchaAI for solving any CAPTCHA that still appears, you get a powerful stack for reliable browser automation.

This guide covers stealth configuration, detection handling techniques, and CaptchaAI integration for reCAPTCHA, Turnstile, and challenge pages.


Why Stealth Matters for CAPTCHA Automation

Standard Puppeteer exposes ~20 detectable signals:

Signal Detection Stealth Fix
navigator.webdriver true in puppeteer Set to undefined
Chrome runtime Missing chrome.runtime Adds mock object
Permissions API Reveals automation Patches responses
WebGL vendor Reports SwiftShader Spoofs real GPU
Plugin count 0 in headless Injects mock plugins
iframe access Blocked cross-origin Fixed handling

Without stealth patches, anti-bot systems serve harder CAPTCHAs or block requests entirely.


Setup

npm install puppeteer-extra puppeteer-extra-plugin-stealth puppeteer

Basic Stealth + CaptchaAI Integration

const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');

puppeteer.use(StealthPlugin());

class StealthCaptchaSolver {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://ocr.captchaai.com';
  }

  async launch(options = {}) {
    this.browser = await puppeteer.launch({
      headless: 'new',
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--disable-blink-features=AutomationControlled',
        '--window-size=1920,1080',
      ],
      ...options,
    });
    return this.browser;
  }

  async solveCaptchaOnPage(page) {
    // Auto-detect CAPTCHA type
    const captchaInfo = await this.detectCaptcha(page);
    if (!captchaInfo) return null;

    console.log(`Detected ${captchaInfo.type} CAPTCHA`);

    const token = await this.solve(captchaInfo);

    // Inject token into page
    await this.injectToken(page, captchaInfo.type, token);

    return token;
  }

  async detectCaptcha(page) {
    return page.evaluate(() => {
      // reCAPTCHA v2
      const recaptchaV2 = document.querySelector('[data-sitekey]');
      if (recaptchaV2) {
        return {
          type: 'recaptcha_v2',
          sitekey: recaptchaV2.getAttribute('data-sitekey'),
          pageUrl: window.location.href,
        };
      }

      // reCAPTCHA v3 (in script)
      const scripts = document.querySelectorAll('script[src*="recaptcha"]');
      for (const script of scripts) {
        const src = script.src;
        const renderMatch = src.match(/render=([A-Za-z0-9_-]+)/);
        if (renderMatch && renderMatch[1] !== 'explicit') {
          return {
            type: 'recaptcha_v3',
            sitekey: renderMatch[1],
            pageUrl: window.location.href,
          };
        }
      }

      // Turnstile
      const turnstile = document.querySelector('.cf-turnstile[data-sitekey]');
      if (turnstile) {
        return {
          type: 'turnstile',
          sitekey: turnstile.getAttribute('data-sitekey'),
          pageUrl: window.location.href,
        };
      }

      return null;
    });
  }

  async solve(captchaInfo) {
    const params = { key: this.apiKey, json: 1 };

    switch (captchaInfo.type) {
      case 'recaptcha_v2':
        params.method = 'userrecaptcha';
        params.googlekey = captchaInfo.sitekey;
        params.pageurl = captchaInfo.pageUrl;
        break;
      case 'recaptcha_v3':
        params.method = 'userrecaptcha';
        params.googlekey = captchaInfo.sitekey;
        params.pageurl = captchaInfo.pageUrl;
        params.version = 'v3';
        params.action = 'verify';
        params.min_score = '0.7';
        break;
      case 'turnstile':
        params.method = 'turnstile';
        params.key = captchaInfo.sitekey;
        params.pageurl = captchaInfo.pageUrl;
        break;
    }

    // Submit
    const submitResp = await fetch(`${this.baseUrl}/in.php`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams(params),
    });
    const submitData = await submitResp.json();
    if (submitData.status !== 1) throw new Error(`Submit: ${submitData.request}`);

    // Poll
    const taskId = submitData.request;
    for (let i = 0; i < 60; i++) {
      await new Promise(r => setTimeout(r, 5000));

      const pollResp = await fetch(
        `${this.baseUrl}/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
      );
      const pollData = await pollResp.json();

      if (pollData.request === 'CAPCHA_NOT_READY') continue;
      if (pollData.status !== 1) throw new Error(`Solve: ${pollData.request}`);
      return pollData.request;
    }

    throw new Error('Timeout');
  }

  async injectToken(page, type, token) {
    switch (type) {
      case 'recaptcha_v2':
      case 'recaptcha_v3':
        await page.evaluate((t) => {
          document.querySelector('#g-recaptcha-response').value = t;
          // Also set in iframe textarea if present
          const iframes = document.querySelectorAll('iframe[src*="recaptcha"]');
          iframes.forEach(iframe => {
            try {
              const textarea = iframe.contentDocument?.querySelector('#g-recaptcha-response');
              if (textarea) textarea.value = t;
            } catch (e) {}
          });
          // Trigger callback
          if (typeof window.___grecaptcha_cfg !== 'undefined') {
            const clients = Object.values(window.___grecaptcha_cfg.clients || {});
            for (const client of clients) {
              const callback = Object.values(client).find(
                v => typeof v === 'object' && v?.callback
              );
              if (callback?.callback) callback.callback(t);
            }
          }
        }, token);
        break;

      case 'turnstile':
        await page.evaluate((t) => {
          const input = document.querySelector('[name="cf-turnstile-response"]');
          if (input) input.value = t;
          // Trigger turnstile callback
          if (window.turnstile) {
            const widgets = document.querySelectorAll('.cf-turnstile');
            widgets.forEach(w => {
              const callback = w.getAttribute('data-callback');
              if (callback && window[callback]) window[callback](t);
            });
          }
        }, token);
        break;
    }
  }

  async close() {
    if (this.browser) await this.browser.close();
  }
}

Complete Workflow Example

async function automateProtectedSite() {
  const solver = new StealthCaptchaSolver('YOUR_API_KEY');

  try {
    await solver.launch();
    const page = await solver.browser.newPage();

    // Set realistic viewport and user agent
    await page.setViewport({ width: 1920, height: 1080 });

    // Navigate to target
    await page.goto('https://example.com/login', { waitUntil: 'networkidle2' });

    // Fill form fields
    await page.type('#email', 'user@example.com', { delay: 50 });
    await page.type('#password', 'password123', { delay: 50 });

    // Solve CAPTCHA
    const token = await solver.solveCaptchaOnPage(page);
    console.log(`CAPTCHA solved: ${token?.substring(0, 50)}...`);

    // Submit form
    await page.click('#login-button');
    await page.waitForNavigation({ waitUntil: 'networkidle2' });

    console.log(`Current URL: ${page.url()}`);

  } finally {
    await solver.close();
  }
}

automateProtectedSite().catch(console.error);

Advanced Stealth Configuration

Selective Plugin Evasions

const StealthPlugin = require('puppeteer-extra-plugin-stealth');

const stealth = StealthPlugin();

// Enable/disable specific evasions
stealth.enabledEvasions.delete('chrome.runtime');
stealth.enabledEvasions.add('navigator.permissions');

puppeteer.use(stealth);

Custom Browser Fingerprint

async function setupFingerprint(page) {
  // Override WebGL renderer
  await page.evaluateOnNewDocument(() => {
    const getParameter = WebGLRenderingContext.prototype.getParameter;
    WebGLRenderingContext.prototype.getParameter = function(parameter) {
      if (parameter === 37445) return 'Intel Inc.';
      if (parameter === 37446) return 'Intel Iris OpenGL Engine';
      return getParameter.call(this, parameter);
    };
  });

  // Set realistic timezone
  await page.emulateTimezone('America/New_York');

  // Set navigator properties
  await page.evaluateOnNewDocument(() => {
    Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 8 });
    Object.defineProperty(navigator, 'deviceMemory', { get: () => 8 });
    Object.defineProperty(navigator, 'platform', { get: () => 'Win32' });
  });
}

Handling Cloudflare Challenge Pages

async function handleCloudflareChallenge(page) {
  // Wait for challenge to appear
  const isChallenged = await page.evaluate(() => {
    return document.title.includes('Just a moment') ||
           document.querySelector('#challenge-running') !== null;
  });

  if (!isChallenged) return;

  console.log('Cloudflare challenge detected');

  // Check for Turnstile widget
  await page.waitForSelector('.cf-turnstile', { timeout: 10000 }).catch(() => null);

  const turnstileKey = await page.evaluate(() => {
    const el = document.querySelector('.cf-turnstile[data-sitekey]');
    return el?.getAttribute('data-sitekey');
  });

  if (turnstileKey) {
    const solver = new StealthCaptchaSolver('YOUR_API_KEY');
    const token = await solver.solve({
      type: 'turnstile',
      sitekey: turnstileKey,
      pageUrl: page.url(),
    });

    await page.evaluate((t) => {
      const input = document.querySelector('[name="cf-turnstile-response"]');
      if (input) {
        input.value = t;
        input.dispatchEvent(new Event('change', { bubbles: true }));
      }
    }, token);

    await page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 30000 });
  }
}

Request Interception for CAPTCHA Detection

async function interceptCaptchaRequests(page) {
  const captchaRequests = [];

  await page.setRequestInterception(true);

  page.on('request', (request) => {
    const url = request.url();

    // Log CAPTCHA-related requests
    if (url.includes('recaptcha') || url.includes('turnstile') || url.includes('hcaptcha')) {
      captchaRequests.push({
        url,
        type: url.includes('recaptcha') ? 'recaptcha' :
              url.includes('turnstile') ? 'turnstile' : 'hcaptcha',
        timestamp: Date.now(),
      });
    }

    // Block unnecessary resources for speed
    const blocked = ['image', 'stylesheet', 'font'];
    if (blocked.includes(request.resourceType()) && !url.includes('captcha')) {
      request.abort();
    } else {
      request.continue();
    }
  });

  return captchaRequests;
}

Multi-Page Session Management

async function multiPageWorkflow(apiKey) {
  const solver = new StealthCaptchaSolver(apiKey);
  await solver.launch();

  const page = await solver.browser.newPage();

  // Step 1: Login page
  await page.goto('https://example.com/login', { waitUntil: 'networkidle2' });
  await page.type('#email', 'user@example.com', { delay: 30 });
  await page.type('#password', 'password', { delay: 30 });
  await solver.solveCaptchaOnPage(page);
  await page.click('#submit');
  await page.waitForNavigation();

  // Step 2: Navigate to protected area
  await page.goto('https://example.com/dashboard', { waitUntil: 'networkidle2' });

  // Step 3: Trigger another CAPTCHA
  await page.click('#export-data');
  await page.waitForSelector('[data-sitekey]', { timeout: 5000 }).catch(() => null);
  await solver.solveCaptchaOnPage(page);

  // Collect cookies for future requests
  const cookies = await page.cookies();
  console.log(`Session cookies: ${cookies.length}`);

  await solver.close();
  return cookies;
}

Detection Testing

Verify your stealth setup works:

async function testStealth() {
  const solver = new StealthCaptchaSolver('YOUR_API_KEY');
  await solver.launch();
  const page = await solver.browser.newPage();

  // Test against popular detection sites
  await page.goto('https://bot.sannysoft.com/');
  await page.screenshot({ path: 'stealth-test.png', fullPage: true });

  // Check specific values
  const results = await page.evaluate(() => ({
    webdriver: navigator.webdriver,
    languages: navigator.languages,
    plugins: navigator.plugins.length,
    platform: navigator.platform,
    hardwareConcurrency: navigator.hardwareConcurrency,
  }));

  console.log('Detection results:', results);
  await solver.close();
}

Troubleshooting

Issue Cause Fix
Still detected as bot Missing stealth evasions Update puppeteer-extra-plugin-stealth to latest
CAPTCHA not found Dynamic loading Add waitForSelector before detection
Token injection fails Shadow DOM or iframe Use page.frames() to access CAPTCHA frame
Browser crashes Memory leak Close pages after use, limit concurrent tabs
Slow performance Blocking resources Use request interception to block images/CSS
ERR_CERT_AUTHORITY_INVALID Self-signed cert Add --ignore-certificate-errors arg

FAQ

Does stealth mode guarantee no CAPTCHAs?

No. Stealth reduces the chance of being served CAPTCHAs, but some sites always show them. CaptchaAI handles the ones that appear.

Should I use headless or headed mode?

Headless 'new' mode with stealth plugin passes most detection. Use headed mode only for debugging.

How often should I rotate user agents?

Rotate per session, not per page. Changing user agent mid-session is a detection signal itself.

Can I use this with Puppeteer Cluster?

Yes. puppeteer-extra works with puppeteer-cluster for parallel browser instances.



Build stealth-optimized browser automation — get your CaptchaAI API key and integrate with Puppeteer Stealth.

Discussions (0)

No comments yet.

Related Posts

Reference CAPTCHA Token Injection Methods Reference
Complete reference for injecting solved CAPTCHA tokens into web pages.

Complete reference for injecting solved CAPTCHA tokens into web pages. Covers re CAPTCHA, Turnstile, and Cloud...

Python Automation Cloudflare Turnstile
Apr 08, 2026
Use Cases Academic Research Web Scraping with CAPTCHA Solving
How researchers can collect data from academic databases, journals, and citation sources protected by CAPTCHAs using Captcha AI.

How researchers can collect data from academic databases, journals, and citation sources protected by CAPTCHAs...

Python Cloudflare Turnstile reCAPTCHA v2
Apr 06, 2026
Use Cases Multi-Step Workflow Automation with CaptchaAI
Manage workflows across multiple accounts on CAPTCHA-protected platforms — , action, and data collection at scale.

Manage workflows across multiple accounts on CAPTCHA-protected platforms — , action, and data collection at sc...

Python Automation Cloudflare Turnstile
Apr 06, 2026
Explainers Mobile Proxies for CAPTCHA Solving: Higher Success Rates Explained
Why mobile proxies produce the lowest CAPTCHA trigger rates and how to use them with Captcha AI for maximum success.

Why mobile proxies produce the lowest CAPTCHA trigger rates and how to use them with Captcha AI for maximum su...

Python Cloudflare Turnstile reCAPTCHA v2
Apr 03, 2026
Tutorials Browser Console CAPTCHA Detection: Finding Sitekeys and Parameters
Use browser Dev Tools to detect CAPTCHA types, extract sitekeys, and find parameters needed for Captcha AI API requests.

Use browser Dev Tools to detect CAPTCHA types, extract sitekeys, and find all parameters needed for Captcha AI...

Automation Cloudflare Turnstile reCAPTCHA v2
Mar 25, 2026
Use Cases Multi-Step Checkout Automation with CAPTCHA Solving
Automate multi-step e-commerce checkout flows that include CAPTCHA challenges at cart, payment, or confirmation stages using Captcha AI.

Automate multi-step e-commerce checkout flows that include CAPTCHA challenges at cart, payment, or confirmatio...

Python Automation Cloudflare Turnstile
Mar 21, 2026
Comparisons Headless vs Headed Chrome for CAPTCHA Solving
Compare headless and headed Chrome for CAPTCHA automation — detection differences, performance trade-offs, and when to use each mode with Captcha AI.

Compare headless and headed Chrome for CAPTCHA automation — detection differences, performance trade-offs, and...

Python Automation Cloudflare Turnstile
Mar 09, 2026
Explainers Rotating Residential Proxies: Best Practices for CAPTCHA Solving
Best practices for using rotating residential proxies with Captcha AI to reduce CAPTCHA frequency and maintain high solve rates.

Best practices for using rotating residential proxies with Captcha AI to reduce CAPTCHA frequency and maintain...

Python Cloudflare Turnstile reCAPTCHA v2
Mar 01, 2026
Use Cases Job Board Scraping with CAPTCHA Handling Using CaptchaAI
Scrape job listings from Indeed, Linked In, Glassdoor, and other job boards that use CAPTCHAs with Captcha AI integration.

Scrape job listings from Indeed, Linked In, Glassdoor, and other job boards that use CAPTCHAs with Captcha AI...

Python Cloudflare Turnstile reCAPTCHA v2
Feb 28, 2026
API Tutorials CaptchaAI API Latency Optimization: Faster Solves
Reduce CAPTCHA solve latency with Captcha AI by optimizing poll intervals, connection pooling, prefetching, and proxy selection.

Reduce CAPTCHA solve latency with Captcha AI by optimizing poll intervals, connection pooling, prefetching, an...

Python Automation Cloudflare Turnstile
Feb 27, 2026
Integrations Axios + CaptchaAI: Solve CAPTCHAs Without a Browser
Use Axios and Captcha AI to solve re CAPTCHA, Turnstile, and image CAPTCHAs in Node.js without launching a browser.

Use Axios and Captcha AI to solve re CAPTCHA, Turnstile, and image CAPTCHAs in Node.js without launching a bro...

Automation All CAPTCHA Types
Apr 08, 2026
Integrations Scrapy + CaptchaAI Integration Guide
Integrate Captcha AI into Scrapy spiders to automatically solve CAPTCHAs during web crawling with middleware and signal handlers.

Integrate Captcha AI into Scrapy spiders to automatically solve CAPTCHAs during web crawling with middleware a...

Automation reCAPTCHA v2 Scrapy
Jan 27, 2026
Integrations Scrapy Spider Middleware for CaptchaAI: Advanced Patterns
Build advanced Scrapy middleware for automatic Captcha AI CAPTCHA solving.

Build advanced Scrapy middleware for automatic Captcha AI CAPTCHA solving. Downloader middleware, signal handl...

Python reCAPTCHA v2 Web Scraping
Apr 04, 2026