Obscura Stealth — Fingerprint Spoofing, TLS Fingerprint, and Anti-Detection

How Obscura Stealth works: built-in browser profile pool, wreq BoringSSL TLS client fingerprint, tracker blocking, and identity consistency configuration.

16Yun Engineering TeamJul 3, 20266 min read

Why Automation Traffic Gets Detected

A headless browser is usually detected at multiple layers simultaneously:

  • TLS layer: HTTP libraries in Go or Python send ClientHello with different cipher suites, ALPN extensions, and elliptic curves than Chrome
  • JS API layer: navigator.webdriver is true, Function.prototype.toString returns non-native code
  • navigator consistency: userAgent says Mozilla/5.0... but platform is Linux, userAgentData is missing
  • Behavioral: Missing or abnormal responses to canvas, audio, and WebGL fingerprinting APIs
  • Network: IP from a datacenter range that does not match the browser fingerprint's geography

Obscura Stealth addresses the first four at the architecture level. The fifth (geographic correlation between IP and identity) must be handled together with a proxy — Stealth itself does not change your exit IP.

Built-in Browser Profile Pool

Obscura ships with a pool of realistic browser profiles (a mix of Windows and macOS, recent Chrome versions). Each profile is internally consistent: navigator.platform, userAgentData (platform and platform version), the UA string, and the WebGL/GPU renderer all agree. Windows profiles report an ANGLE Direct3D11 renderer, macOS profiles report an ANGLE Metal renderer — exactly what real Chrome does on each platform.

That internal consistency is the core of anti-detection. Many naive spoofing setups only patch the UA string while leaving navigator.platform and the GPU renderer at the underlying library's defaults; a detection script that cross-checks these surfaces spots the mismatch immediately.

By default a single stable profile is used. Environment variables control profile selection:

# Pin a specific profile by index
OBSCURA_PROFILE=2 obscura serve
 
# Random profile per browser context (opt-in)
OBSCURA_ROTATE_PROFILE=1 obscura serve

Rotating identities is itself a signal — real devices do not change fingerprints constantly. Rotation is off by default. Only enable it when one exit IP genuinely needs to back multiple independent identities, which is rare.

TLS Fingerprint Spoofing

This is Stealth's core capability. It uses the wreq crate with BoringSSL, configured to match Chrome's:

  • Cipher suite order
  • TLS version preferences
  • ALPN protocol list
  • Elliptic curve selection
# Stealth build requires cmake for BoringSSL
cargo build --release --features stealth
 
# TLS ClientHello now matches real Chrome
obscura fetch https://example.16yun.cn --stealth

Outside Stealth, the engine uses rustls, which needs neither cmake nor OpenSSL. One detail worth knowing: scripted fetch()/XHR go through the same stealth client, so subresource requests carry the same TLS fingerprint as the navigation request — you never end up with "main request looks like Chrome, subresource requests look like a Rust library".

Build note: wreq's prefix-symbols feature only renames BoringSSL exported symbols correctly on Linux and Android; on macOS the configuration without prefix-symbols is used.

JS API Layer

Stealth hides automation signals at the V8 level.

navigator.webdriver

// Without stealth: exposed
navigator.webdriver  // true
 
// With stealth: matches real Chrome
navigator.webdriver  // undefined

Function.prototype.toString

// Without stealth: toString leaks code
navigator.webdriver.toString()  // "function webdriver() { [native code] }" or custom
 
// With stealth: standard native marker
Function.prototype.toString.call(navigator.webdriver)  // "function () { [native code] }"

event.isTrusted

Dispatched synthetic events have event.isTrusted = true, so page scripts do not flag them as automated.

Object.keys(window)

Hidden internal properties are not exposed by Object.keys(window).

Tracker Blocking

Stealth ships with Peter Lowe's ad and tracker domain list — 3500+ domains blocked at the net layer before a request ever leaves the process:

# Blocks automatically:
# - Analytics (Google Analytics, Mixpanel, Hotjar)
# - Ad networks (DoubleClick, Criteo, Adnxs)
# - Social tracking (Facebook.net)
# - Fingerprinting scripts

Supports exact match and subdomain wildcard matching. Because the block happens before dispatch, blocked requests never reach V8 or the DOM, which both saves bandwidth and avoids triggering third-party anti-scraping logic.

Identity Consistency

JS-layer masking alone is not enough. The real difficulty of Stealth is that timezone, geolocation, proxy region, and profile must all agree — any mismatch is itself a detection signal.

# Complete anti-detection config
OBSCURA_TIMEZONE=America/New_York \
OBSCURA_GEOLOCATION="40.7128,-74.0060" \
OBSCURA_PROFILE=2 \
  obscura serve --stealth --proxy http://user:pass@proxy.16yun.cn:8888

OBSCURA_TIMEZONE: default Europe/Berlin. Affects Date.getTimezoneOffset, Intl.DateTimeFormat, and timezone-related APIs. The engine pins the process timezone before V8/ICU reads it, so every Date interface reports the same zone.

OBSCURA_GEOLOCATION: configures the coordinates the navigator.geolocation shim reports, as lat,lon. Reports a fixed default when unset.

Profile / Timezone / Proxy alignment: if your proxy exits in New York, pick a macOS profile (common for North American business users), set the timezone to America/New_York, and the geolocation to 40.7128,-74.0060. Any contradiction becomes a detection signal.

This is the key idea: Stealth provides "one self-consistent identity", not "an unidentifiable identity". What detectors actually do is cross-validation — UA says Windows, timezone says Shanghai, IP exits in Frankfurt. Those three disagreeing is far more revealing than any single anomalous feature. So in production, the heart of anti-detection configuration is not turning Stealth on; it is aligning profile, timezone, geolocation, and proxy exit onto one coherent identity picture.

Verifying Your Stealth Configuration

Once Stealth is configured, the first thing to do is verify it actually works before shipping to production. A few self-checks:

Check navigator.webdriver. Run --eval against a target page:

obscura fetch https://example.16yun.cn --stealth --eval "navigator.webdriver"

Without Stealth this returns true; with Stealth active it should return undefined.

Check the TLS fingerprint. Point the engine at a service that echoes the JA3 fingerprint (set one up yourself or use a trusted debugging endpoint) and compare the ClientHello hash with Stealth on and off. With Stealth on, it should match real Chrome's JA3, not the default rustls or reqwest fingerprint.

Cross-check consistency. Run a snippet of JS that prints navigator.userAgent, navigator.platform, navigator.userAgentData, and the timezone (Intl.DateTimeFormat().resolvedOptions().timeZone) all at once, and eyeball whether the four point to one coherent identity. Any mismatch — UA says Windows while timezone says Shanghai — is a flaw a detector will catch.

Compare success rates with and without Stealth. Against your real target site, run one batch with Stealth and one without, and compare block rates. If the two are about the same, the site's detection point is outside Stealth's coverage (probably IP reputation or behavioral analysis), so shift to proxies or behavioral simulation. If Stealth clearly improves the success rate, TLS/JS fingerprinting is the main detection vector and your configuration is effective.

Limitations

Stealth does not handle:

  • Cloudflare interactive challenges (Turnstile, JS Challenge, CAPTCHA)
  • Datadome and Akamai bot managers
  • Any CAPTCHA system
  • IP-based rate limiting (needs proxies)

For these, pair Stealth with residential proxies or a fingerprint browser.

Best Practice

OBSCURA_TIMEZONE=Asia/Shanghai \
OBSCURA_GEOLOCATION="31.2304,121.4737" \
OBSCURA_PROFILE=0 \
  obscura serve \
    --stealth \
    --proxy http://user:pass@proxy.16yun.cn:8888

Practical recommendations:

  • Pick the proxy exit region first, then the identity. Identity follows the IP, not the other way around. If you ride a residential proxy exiting in the US, choose a macOS profile, a US timezone, and US geolocation coordinates.
  • Keep the identity stable. Do not switch profile or timezone mid-session for one exit IP. Frequent identity changes are themselves an anomaly signal.
  • Leave rotation off. Unless you specifically need one IP to back multiple identities (rare), keep the default single stable profile.
  • Pair with a dedicated proxy. IPs in a shared pool may already be flagged by the target site from other users' traffic. For high anti-detection requirements, a dedicated proxy's IP cleanliness is more reliable.

Summary

Obscura Stealth builds anti-detection at four layers:

  1. TLS layer: BoringSSL wreq client matching Chrome's fingerprint
  2. Profile layer: a realistic profile pool with internally consistent navigator properties
  3. JS API layer: webdriver hiding, native code masking, event.isTrusted
  4. Network layer: 3500+ domain tracker blocking

It is not a universal anti-detection solution, but for the majority of defenses based on JS-surface detection and TLS fingerprinting, Stealth is sufficient. What ultimately determines anti-detection effectiveness is whether profile, timezone, geolocation, and proxy exit are aligned onto a single self-consistent identity.

Need an enterprise proxy plan?

We can tailor architecture to your target domains, concurrency, and reliability goals.