Stealth

Platform Emulation

A real browser presents a consistent identity across every signal — User-Agent, client hints, navigator properties, and sec-fetch metadata all agree on the same platform. If a request claims to be Chrome on macOS via User-Agent but sends sec-ch-ua-platform: "Windows", anti-bot systems flag it immediately. RezoStealth resolves a platform for each profile and ensures every value is consistent.

Platform Inference

When you do not explicitly set a platform in RezoStealthOptions, the resolver infers one from the profile:

Profile TypeInferred Platform
Mobile profiles with Android UAandroid
Mobile profiles with iOS UAios
Desktop Safari profilesmacos (always)
Other desktop profilesRandom from windows, macos, linux
import { RezoStealth } from 'rezo/stealth';

// Mobile profile -> always android
const mobile = new RezoStealth('chrome-131-android');
// Platform: android

// Safari desktop -> always macOS
const safari = new RezoStealth('safari-18.2');
// Platform: macos

// Chrome desktop -> random OS each time
const chrome = new RezoStealth('chrome-131');
// Platform: windows | macos | linux (random)

Explicit Platform Override

Force a specific platform regardless of profile:

const stealth = new RezoStealth({
  profile: 'chrome-131',
  platform: 'linux'
});
// All platform signals will be Linux-consistent

Platform-Consistent Values

Once a platform is resolved, the following values are set to match:

sec-ch-ua-platform

The sec-ch-ua-platform client hint header (Chromium browsers only):

PlatformValue
windows"Windows"
macos"macOS"
linux"Linux"
android"Android"
ios"iOS"

sec-ch-ua-mobile

Indicates whether the client is a mobile device:

PlatformValue
Desktop (windows, macos, linux)?0
Mobile (android, ios)?1

The emulated navigator.platform value for JavaScript environment emulation:

PlatformValue
windowsWin32
macosMacIntel
linuxLinux x86_64
androidLinux armv81
iosiPhone

maxTouchPoints

The emulated navigator.maxTouchPoints:

PlatformValue
Desktop (windows, macos, linux)0
Mobile (android, ios)5

User-Agent Selection

The profile contains User-Agent strings for each platform. The resolver picks the one matching the resolved platform:

// A Chrome 131 profile has:
userAgents: {
  windows: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...',
  macos: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ...',
  linux: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 ...',
  android: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 ...'
}

// If platform resolves to 'macos', the macOS UA string is used

Sec-Fetch Headers

Real browsers send sec-fetch-* metadata headers on navigation requests. RezoStealth includes these by default with navigation context values:

// Default sec-fetch headers applied by RezoStealth:
{
  'sec-fetch-site': 'none',       // Direct navigation (no referrer)
  'sec-fetch-mode': 'navigate',   // Full page navigation
  'sec-fetch-user': '?1',         // User-triggered request
  'sec-fetch-dest': 'document'    // Requesting an HTML document
}

These defaults model a user typing a URL into the address bar. For AJAX-style requests, you can override them:

const rezo = new Rezo({
  stealth: RezoStealth.chrome()
});

// Override for API requests
const res = await rezo.get('https://api.example.com/data', {
  headers: {
    'sec-fetch-site': 'same-origin',
    'sec-fetch-mode': 'cors',
    'sec-fetch-dest': 'empty'
  }
});

The upgrade-insecure-requests: 1 header is also included by default, as all modern browsers send it on navigation.

Header Ordering

Anti-bot systems analyze the order in which headers appear. Each browser sends headers in a characteristic sequence. Profiles store this as a headerOrder array:

// Chrome header order example:
headerOrder: [
  'host',
  'sec-ch-ua',
  'sec-ch-ua-mobile',
  'sec-ch-ua-platform',
  'upgrade-insecure-requests',
  'user-agent',
  'accept',
  'sec-fetch-site',
  'sec-fetch-mode',
  'sec-fetch-user',
  'sec-fetch-dest',
  'accept-encoding',
  'accept-language',
  'cookie'
]

Firefox and Safari use different orderings. When the resolved profile is applied to a request, headers are reordered to match the browser’s native sequence.

HTTP/2 Pseudo-Header Ordering

HTTP/2 frames include pseudo-headers (:method, :authority, :scheme, :path) that must be sent before regular headers. Browsers send these in different orders:

Browser FamilyOrder
Chrome, Edge, Opera, Brave:method, :authority, :scheme, :path
Firefox:method, :path, :authority, :scheme
Safari:method, :scheme, :path, :authority

This is encoded in profiles as a shorthand (masp, mpas, mspa) and expanded into full pseudo-header names during resolution.

Full Example

Putting it all together — a stealth request with fully consistent platform emulation:

import { Rezo } from 'rezo';
import { RezoStealth } from 'rezo/stealth';

const rezo = new Rezo({
  stealth: new RezoStealth({
    family: 'chrome',
    platform: 'macos'
  })
});

const res = await rezo.get('https://protected-site.com');

// The request sends:
// - macOS Chrome User-Agent
// - sec-ch-ua-platform: "macOS"
// - sec-ch-ua-mobile: ?0
// - navigator.platform: MacIntel
// - maxTouchPoints: 0
// - Chrome header ordering
// - Chrome TLS cipher suite order
// - Chrome HTTP/2 SETTINGS values
// - Chrome pseudo-header order (:method :authority :scheme :path)
// - sec-fetch-site: none, sec-fetch-mode: navigate, etc.