Skip to main content

Anti-Detection Best Practices

Meshbrow handles most anti-detection automatically, but your browsing behavior still matters. This guide covers what Meshbrow does for you and what you need to do yourself.

What Meshbrow Handles Automatically

Browser Fingerprint

Canvas noise, WebGL spoofing, font enumeration, plugin list, screen resolution — all generated consistently per session.

Navigator Properties

platform, hardwareConcurrency, deviceMemory, languages, webdriver flag — all patched to match the fingerprint.

WebRTC Leak Prevention

Local IP addresses are masked. WebRTC candidates reflect the proxy IP only.

TLS Fingerprint

JA3/JA4 fingerprints match real Chrome browsers. No detectable automation signatures.

Timezone & Locale

Automatically matched to proxy geo-location. Intl.DateTimeFormat and Date are consistent.

Chrome Flags

No --enable-automation, no cdc_ driver properties, no leaked DevTools protocol indicators.

What You Need to Do

1. Behave Like a Human

Anti-detect isn’t just about fingerprints — it’s about behavior.
// ❌ BAD: Machine-like rapid navigation
await page.goto('https://example.com/page-1');
await page.goto('https://example.com/page-2');
await page.goto('https://example.com/page-3');

// ✅ GOOD: Human-like browsing with delays
await page.goto('https://example.com/page-1');
await page.waitForTimeout(randomDelay(2000, 5000));
await page.goto('https://example.com/page-2');
await page.waitForTimeout(randomDelay(1500, 4000));

function randomDelay(min: number, max: number) {
  return Math.floor(Math.random() * (max - min) + min);
}

2. Simulate Mouse Movement

// Move mouse naturally before clicking
async function humanClick(page, selector) {
  const element = await page.$(selector);
  const box = await element.boundingBox();

  // Move to element with natural curve
  await page.mouse.move(
    box.x + box.width * Math.random(),
    box.y + box.height * Math.random(),
    { steps: Math.floor(Math.random() * 10) + 5 }
  );

  await page.waitForTimeout(randomDelay(100, 300));
  await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
}

3. Scroll Naturally

// ❌ BAD: Jump to bottom
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));

// ✅ GOOD: Scroll incrementally
async function humanScroll(page) {
  const scrollHeight = await page.evaluate(() => document.body.scrollHeight);
  let currentPosition = 0;

  while (currentPosition < scrollHeight) {
    const scrollAmount = Math.floor(Math.random() * 300) + 100;
    currentPosition += scrollAmount;
    await page.evaluate((y) => window.scrollTo(0, y), currentPosition);
    await page.waitForTimeout(randomDelay(200, 800));
  }
}

4. Use Realistic Session Durations

// ❌ BAD: Extract and immediately close
const data = await page.textContent('.data');
await client.sessions.destroy(session.id);

// ✅ GOOD: Spend realistic time on page
await page.waitForTimeout(randomDelay(5000, 15000)); // "Read" the page
const data = await page.textContent('.data');
await page.waitForTimeout(randomDelay(2000, 5000));
await client.sessions.destroy(session.id);

5. Match Proxy Geo with Content

// ❌ BAD: US proxy visiting Japanese site with English locale
const session = await client.sessions.create({
  proxy: { type: 'residential', country: 'US' },
  fingerprint: { locale: 'en-US', timezone: 'America/New_York' },
});
await page.goto('https://rakuten.co.jp');

// ✅ GOOD: Japanese proxy for Japanese site
const session = await client.sessions.create({
  proxy: { type: 'residential', country: 'JP' },
  fingerprint: { locale: 'ja-JP', timezone: 'Asia/Tokyo' },
});
await page.goto('https://rakuten.co.jp');

Common Detection Vectors

VectorRiskMeshbrow Mitigation
Canvas fingerprintHighSubtle noise injection per session
WebGL rendererHighSpoofed vendor/renderer strings
navigator.webdriverHighCompletely removed
TLS fingerprint (JA3)HighMatches real Chrome
IP reputationMediumMulti-provider proxy pool with scoring
Behavioral analysisMediumYour responsibility
Request patternsMediumAdd human-like delays
Header orderLowMatches real Chrome exactly
Font enumerationLowRealistic font sets per OS

Testing Your Anti-Detection

Use these sites to verify your setup:
const testSites = [
  'https://nowsecure.nl',           // General stealth test
  'https://bot.sannysoft.com',       // Comprehensive checks
  'https://browserleaks.com',        // Detailed fingerprint info
  'https://creepjs.com',             // Advanced fingerprinting
  'https://fingerprintjs.github.io/fingerprintjs/', // FingerprintJS
];

for (const url of testSites) {
  await page.goto(url);
  await page.screenshot({ path: `test-${new URL(url).hostname}.png` });
}

Stealth Levels Explained

LevelWhat It DoesWhen to Use
noneNo anti-detection patchesTesting, development
standardBasic patches (webdriver, plugins, languages)Low-security sites
maxFull suite (canvas, WebGL, TLS, fonts, everything)High-security sites, captcha-protected pages
Always start with stealth: "max". Only reduce if you need faster session launch times for known low-security targets.