# Stealth Browser

> Bypass Cloudflare Turnstile and other anti-bot detection using puppeteer-extra + stealth plugin. Access websites that block headless browsers.

- Skill: `succ985/stealth-browser` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add succ985/stealth-browser`
- Raw SKILL.md: https://api.skillmd.com/api/skills/succ985/stealth-browser/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: succ985 (https://skillmd.com/u/succ985)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/succ985/stealth-browser

---


# Stealth Browser 🕵️

Bypass Cloudflare Turnstile and other anti-bot detection systems using puppeteer-extra with stealth plugin. Access websites that block headless browsers.

## When to Use

- Websites protected by Cloudflare Turnstile
- Sites that block automated/headless browsers
- Need screenshots or visual content
- Require JavaScript execution and interaction
- Anti-scraping detection systems

## Known Working Sites

- ✅ linux.do (Cloudflare Turnstile)
- ✅ Boss直聘 (滑块验证码)
- ✅ 拉勾网 (阿里云滑块验证)
- ✅ Most Cloudflare-protected websites

## Installation

```bash
# Install dependencies
npm install puppeteer-extra puppeteer-extra-plugin-stealth

# The skill wrapper is ready to use
```

## Quick Start

### Command Line

```bash
# Quick visit and screenshot
node /root/.openclaw/workspace/skills/stealth-browser/wrapper.js \
  https://linux.do \
  linux-do-screenshot.png
```

### In Code

```javascript
const { quickVisit, StealthBrowser } = require('/root/.openclaw/workspace/skills/stealth-browser/wrapper.js');

// Method 1: Quick visit
const result = await quickVisit('https://linux.do', 'screenshot.png');
console.log(result);
// { success: true, title: 'LINUX DO - ...', screenshotPath: '...' }

// Method 2: Full control
const browser = new StealthBrowser();
await browser.launch();
await browser.goto('https://linux.do');
const title = await browser.getTitle();
await browser.screenshot('homepage.png');
await browser.close();
```

### In Subagent

```javascript
const { quickVisit } = require('/root/.openclaw/workspace/skills/stealth-browser/wrapper.js');

const result = await quickVisit('https://linux.do', 'linux-do.png');
if (result.success) {
  // Send screenshot via message tool
  message({
    action: "send",
    channel: "telegram",
    media: result.screenshotPath,
    message: "Linux.do homepage"
  });
}
```

## API Reference

### quickVisit(url, filename)

Quick visit a website and take a screenshot.

**Parameters:**
- `url` (string): Target URL
- `filename` (string): Screenshot filename

**Returns:**
```javascript
{
  success: boolean,
  title: string,
  screenshotPath: string
}
```

### StealthBrowser Class

#### Constructor

```javascript
new StealthBrowser(options)
```

**Options:**
- `headless`: 'new' | true | false (default: 'new')
- `viewport`: { width, height } (default: { width: 1920, height: 1080 })
- `userAgent`: string (default: Chrome 131)
- `screenshotPath`: string (default: '/root/.openclaw/media/browser/')

#### Methods

- `launch()` - Start browser
- `goto(url, options?)` - Navigate to URL
- `screenshot(filename)` - Take screenshot
- `getTitle()` - Get page title
- `getContent()` - Get HTML content
- `evaluate(fn, ...args)` - Execute JavaScript
- `checkSuccess(keywords)` - Check if visit succeeded
- `close()` - Close browser
- `visitAndScreenshot(url, filename)` - Shortcut method

## How It Works

### Why OpenClaw Default Browser Fails

1. `navigator.webdriver = true` - Explicitly identifies as automated browser
2. Uses legacy headless mode with obvious fingerprints
3. Missing real browser fingerprints
4. No plugin list or language settings

### How StealthPlugin Solves It

1. **Hides automation indicators:**
   - Changes `navigator.webdriver` to `undefined`
   - Overrides `Function.prototype.toString`
   - Fixes iframe detection

2. **Mimics real fingerprints:**
   - Adds real plugin list
   - Simulates language settings
   - Fixes `window.outerWidth/Height`
   - Adds touch support

3. **Uses new headless mode:**
   - `headless: 'new'` uses real Chrome rendering engine
   - Much harder to detect

4. **Disables automation features:**
   - `--disable-blink-features=AutomationControlled`
   - `--no-sandbox`
   - `--disable-setuid-sandbox`

## Detection Point Comparison

| Detection Point | OpenClaw Default | StealthPlugin |
|----------------|-----------------|---------------|
| navigator.webdriver | true ❌ | undefined ✅ |
| headless fingerprints | obvious ❌ | hidden ✅ |
| browser fingerprints | missing ❌ | complete ✅ |
| plugin list | empty ❌ | real ✅ |
| window.outerWidth | 0 ❌ | 1920 ✅ |

## Examples

### Example 1: Basic Visit

```javascript
const { StealthBrowser } = require('/root/.openclaw/workspace/skills/stealth-browser/wrapper.js');

const browser = new StealthBrowser();
await browser.launch();
await browser.goto('https://linux.do');
const title = await browser.getTitle();
console.log('Title:', title);
await browser.screenshot('linux-do.png');
await browser.close();
```

### Example 2: Multiple Pages

```javascript
const { StealthBrowser } = require('/root/.openclaw/workspace/skills/stealth-browser/wrapper.js');

const browser = new StealthBrowser();
await browser.launch();

const urls = [
  'https://linux.do',
  'https://linux.do/latest',
  'https://linux.do/c/develop/4'
];

for (const url of urls) {
  await browser.goto(url);
  const title = await browser.getTitle();
  console.log('Title:', title);
  const filename = url.replace(/https?:\/\/|\/$/g, '').replace(/\//g, '-') + '.png';
  await browser.screenshot(filename);
}

await browser.close();
```

### Example 3: Custom Configuration

```javascript
const { StealthBrowser } = require('/root/.openclaw/workspace/skills/stealth-browser/wrapper.js');

const browser = new StealthBrowser({
  headless: 'new',
  viewport: { width: 2560, height: 1440 },
  userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
});

await browser.goto('https://linux.do', { waitMs: 5000 });
const content = await browser.getContent();
console.log('Content length:', content.length);
await browser.close();
```

## Known Issues

1. **Requires graphics environment:** Headed mode needs X server
2. **Resource intensive:** More resource usage than pure text fetching
3. **Slower speed:** Requires launching full browser

## Alternative Solutions

If you only need text content, consider using r.jina.ai proxy:

```bash
curl -s "https://r.jina.ai/http://linux.do/latest"
```

**Pros:**
- Faster
- Lighter
- 100% reliable

**Cons:**
- Cannot take screenshots
- Cannot interact
- Depends on third-party service

## Files

- `wrapper.js` - Main wrapper script
- `examples.js` - Usage examples
- `README.md` - Detailed documentation

## Changelog

- 2026-02-11: Created skill with puppeteer-extra + stealth plugin
- 2026-02-11: Successfully bypassed Cloudflare Turnstile on linux.do
- 2026-02-11: Added comprehensive API and examples

## References

- [puppeteer-extra](https://github.com/berstend/puppeteer-extra)
- [puppeteer-extra-plugin-stealth](https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth)
- [Cloudflare Turnstile](https://developers.cloudflare.com/turnstile/)
