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 Type | Inferred Platform |
|---|---|
| Mobile profiles with Android UA | android |
| Mobile profiles with iOS UA | ios |
| Desktop Safari profiles | macos (always) |
| Other desktop profiles | Random 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):
| Platform | Value |
|---|---|
windows | "Windows" |
macos | "macOS" |
linux | "Linux" |
android | "Android" |
ios | "iOS" |
sec-ch-ua-mobile
Indicates whether the client is a mobile device:
| Platform | Value |
|---|---|
Desktop (windows, macos, linux) | ?0 |
Mobile (android, ios) | ?1 |
navigator.platform
The emulated navigator.platform value for JavaScript environment emulation:
| Platform | Value |
|---|---|
windows | Win32 |
macos | MacIntel |
linux | Linux x86_64 |
android | Linux armv81 |
ios | iPhone |
maxTouchPoints
The emulated navigator.maxTouchPoints:
| Platform | Value |
|---|---|
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 Family | Order |
|---|---|
| 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.