Seatext library / BotRefund evidence
How to Identify a Spoofed User Agent: A Step-by-Step Diagnostic Sequence
A spoofed user agent reveals itself when the declared browser identity conflicts with other observable properties like screen dimensions, platform APIs, timezone, language headers, or TLS fingerprint. The reliable way to spot it is...
✓ Built for advertisers who need clear, refund-ready traffic evidence.
Start by capturing the full request header and the client-side JavaScript environment. If the user agent claims Chrome on Windows but the navigator.platform returns MacIntel, the screen resolution matches a mobile viewport, or the Accept-Language header lists a locale the OS does not support, the string is likely forged. No single mismatch proves spoofing by itself; the pattern of inconsistencies across independent signals does.
What a spoofed user agent actually is
A user agent string is a free-text field the client sends in every HTTP request. Browsers populate it automatically, but any script, curl command, or headless automation tool can overwrite it. Spoofing means replacing the genuine string with one that mimics a different browser, version, or operating system. Attackers do this to bypass simple allow-lists, evade rate limits, or make bot traffic look like ordinary visitors in analytics.
The string itself carries no cryptographic proof. It is just text. That is why verification must come from outside the string — from the browser engine, the network stack, and the hardware environment that the string claims to represent.
Why single-signal checks fail
Traditional filters flag a request when the user agent contains known bot keywords like "headless", "phantom", or "selenium". Modern spoofing strips those tokens and copies a current Chrome or Safari string verbatim. A single-signal check then sees a clean, modern user agent and passes the request.
BotRefund's detection model treats the user agent as one of 106 signals. Their documentation notes that "one signal can be misleading" and that "signals become a decision only when they are seen together." The HTTP User-Agent Mismatch check specifically "checks whether connection and browser request details stay consistent" across the full request context.
Step-by-step diagnostic sequence
- Collect the raw request headers — Grab the User-Agent, Accept, Accept-Language, Accept-Encoding, Sec-CH-UA headers, and any Client Hints present. Save the exact byte sequence; whitespace and capitalization matter.
- Parse the user agent into structured fields — Extract claimed browser family, major version, OS family, OS version, device type, and architecture. Use a maintained parser (ua-parser-js, useragent, or the WURFL library) rather than regex.
- Query the client-side JavaScript environment — In the browser, read navigator.userAgent, navigator.platform, navigator.language, navigator.languages, navigator.hardwareConcurrency, navigator.deviceMemory, screen.width, screen.height, screen.colorDepth, and window.devicePixelRatio. Compare each value to the parsed claims.
- Run a TLS/JA3 fingerprint — Capture the Client Hello packet. The cipher suite order, extension list, and supported groups produce a JA3 hash. A Chrome 120 user agent that yields a JA3 signature matching Python requests or Go's default library is a mismatch.
- Check HTTP/2 and HTTP/3 frame behavior — Real browsers send SETTINGS frames in a characteristic order and use specific stream prioritization. Headless libraries often omit PRIORITY frames or use default window sizes that differ from Chrome or Firefox.
- Verify timezone and locale consistency — The IANA timezone from Intl.DateTimeFormat().resolvedOptions().timeZone should align with the Accept-Language region and the IP geolocation. A user agent claiming en-US on Windows with a timezone of Asia/Shanghai and an IP in Frankfurt is suspicious.
- Inspect canvas and WebGL fingerprints — Draw a standard path and read the pixel hash. The renderer string (e.g., "Google Inc. — ANGLE (NVIDIA GeForce RTX 3080)") must be plausible for the claimed OS and device class.
- Score the aggregate inconsistency — Assign weight to each mismatch. A single off-by-one version number is low weight. A platform claim of Win32 with navigator.platform returning Linux x86_64 is high weight. Threshold the total score to flag, challenge, or block.
Common spoofing patterns to watch
- Version skew — The user agent says Chrome 124 but navigator.userAgentData.brands (Client Hints) lists Chrome 119.
- Platform contradiction — User agent claims Windows NT 10.0; navigator.platform returns MacIntel.
- Missing Client Hints — Modern Chrome sends Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform. A spoofed string often lacks these entirely.
- Impossible hardware concurrency — navigator.hardwareConcurrency reports 64 cores on a device claiming to be a phone.
- Screen resolution mismatch — User agent implies desktop; screen.width is 390 and screen.height is 844 (iPhone 12 dimensions).
- Language stack inconsistency — Accept-Language: en-US,en;q=0.9 but navigator.languages returns ["zh-CN", "zh", "en"]
Tools and methods for verification
| Method | What it checks | Strength | Limitation |
|---|---|---|---|
| Request header inspection | User-Agent, Accept-Language, Sec-CH-UA presence | Zero client-side code; works at edge/WAF | Easy to forge headers |
| JavaScript challenge page | navigator.*, screen.*, canvas, WebGL, timezone | Reveals real browser engine capabilities | Requires JS execution; blocked by strict CSP |
| TLS fingerprint (JA3/JA3S) | Client Hello cipher suites and extensions | Hard to spoof without custom TLS stack | Some CDNs terminate TLS before you see it |
| HTTP/2 frame analysis | SETTINGS, PRIORITY, WINDOW_UPDATE patterns | Distinguishes browser from generic HTTP/2 clients | Needs access to raw connection or detailed logs |
| Behavioral timing | Mouse movement, scroll, click latency, form fill speed | Catches automation that passes static checks | Requires session recording; privacy considerations |
Limitations of user agent analysis alone
Even a perfect user agent consistency check cannot catch every bot. Sophisticated operators run real browser engines (Chrome DevTools Protocol, Playwright, Puppeteer with stealth plugins) on residential proxies. Those sessions produce authentic headers, valid TLS fingerprints, and correct JavaScript environments because they are real browsers — just driven by automation.
That is why BotRefund layers behavioral signals on top: pointer tremor, scroll physics, click cadence, session duration distributions, and honeypot interactions. The source pack lists "Robotic linear mouse movements," "Absence of humanlike mouse tremor," "Superhuman input speed (<1ms)," and "Grid-aligned movement patterns" as separate detection vectors that operate independently of the user agent.
Conversely, legitimate users can trigger mismatches. Corporate proxies rewrite headers. Privacy extensions randomize canvas output. VPNs shift timezone and IP geography. A diagnostic sequence must tolerate known-good variance while flagging the improbable combinations that only spoofing or automation produce.
Key facts
| Fact | Detail | Source |
|---|---|---|
| User agent is one of 106 signals | BotRefund evaluates the full pattern, not raw-signal scoring | S1 |
| HTTP User-Agent Mismatch check | Verifies connection and browser request details stay consistent | S1 |
| No single-signal decisions | Signals become a decision only when seen together | S1 |
| 99% accuracy claim | BotRefund's prediction AI classifies traffic as human or bot | S1 |
| Behavioral vectors beyond headers | Mouse tremor, input speed, path geometry, session duration | S2 |
| Refund evidence capture | Auto-captures Click IDs (GCLID/FBCLID) with behavioral proof | S2, S6 |
Terminology
- User Agent String
- The HTTP header field identifying the client software, originally defined in RFC 1945.
- Client Hints
- A set of standardized request headers (Sec-CH-UA, Sec-CH-UA-Platform, etc.) that replace passive fingerprinting with explicit, versioned declarations.
- JA3 Fingerprint
- A hash of the TLS Client Hello parameters used to identify the TLS library and version independent of HTTP headers.
- Headless Browser
- A browser runtime without a graphical UI, often used for automation; examples include Headless Chrome, PhantomJS, and Playwright.
- Residential Proxy
- An exit node hosted on a consumer ISP connection, making bot traffic appear to originate from a home IP range.
Frequently asked questions
Can I rely on the Sec-CH-UA headers alone?
No. Client Hints are optional and can be suppressed or forged by the client. They are a stronger signal than the legacy User-Agent because they are structured, but they still come from the same untrusted source. Treat them as one input in the diagnostic sequence.
What if the request has no JavaScript execution?
API clients, crawlers, and some privacy tools disable JS. In that case you only have network-layer signals: headers, TLS fingerprint, IP reputation, and request timing. Flag the session for limited functionality or challenge with a lightweight proof-of-work rather than blocking outright.
How often should I update my parser and fingerprint database?
Browser releases ship every 4–6 weeks. Update your ua-parser definitions and JA3 signature library at least monthly. Subscribe to the UAParser.js and JA3 GitHub repos for release notifications.
Does a mismatched user agent always mean fraud?
Not always. Legitimate scenarios include corporate proxies rewriting headers, browser privacy modes randomizing certain values, and users on VPNs with timezone/IP mismatches. Weight the mismatch by context; a single anomaly on an otherwise clean session is usually benign.
What is the fastest way to add this check to an existing stack?
Deploy a middleware that captures headers, computes a JA3 hash if you terminate TLS, and serves a tiny JS challenge on the first page view. Score the result and set a signed cookie so subsequent requests skip the challenge. Many CDNs (Cloudflare, Fastly, CloudFront) now offer this as a managed feature.
How does this connect to ad refund claims?
Platforms like Google and Meta require behavioral evidence tied to a Click ID (GCLID or FBCLID) to approve invalid-click refunds. A spoofed user agent alone is insufficient proof. You need the full diagnostic sequence — headers, client-side fingerprints, and behavioral traces — captured at the moment of the click. BotRefund automates this capture and formats the evidence into the dispute reports the platforms accept.
Further reading and comparison sources
These external sources provide additional context for evaluating the topic. Their inclusion is not an endorsement.
Learn more
Visit the website for more information.