User-Agent Reduction and Client Hints (Sec-CH-UA): Preserving Device Model and OS Targeting in OpenRTB 2.6

Every mobile web ad request originating from Chrome 110 or later on an Android handset transmits the exact same hardware identifier in its legacy HTTP

Macro 3D comparison of an opaque frosted grey legacy user-agent plate on the left and a multi-faceted crystal-clear sapphire Sec-CH-UA plate unlocked by a golden server key beam on the right.

Every mobile web ad request originating from Chrome 110 or later on an Android handset transmits the exact same hardware identifier in its legacy HTTP User-Agent header: a single uppercase letter K. Whether the user is browsing on a flagship Samsung Galaxy S24 Ultra running Android 14 or an entry-level Motorola handset running Android 10, the raw string delivered to ad servers and Supply-Side Platforms (SSPs) reads Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{major}.0.0.0 Mobile Safari/537.36. For programmatic architectures that still rely on server-side regex parsing of the legacy User-Agent header or the client-side navigator.userAgent DOM property, hardware model granularity, minor browser build versions, and true operating system release numbers have been completely erased.

When device model and OS version signals vanish from outbound bid requests, buy-side bidding algorithms lose a primary proxy for consumer purchasing power, hardware rendering capability, and fraud verification. Tier-1 Demand-Side Platforms (DSPs) use handset pricing tiers to segment mobile web audiences when third-party cookies and mobile ad IDs (MAIDs) are unavailable. Without explicit telemetry differentiating a $1,200 flagship device from a $90 prepaid phone, DSP bid shading algorithms automatically price the impression down to the lowest common denominator.

Restoring this lost yield requires transitioning both publisher server configurations and programmatic wrapper deployments from passive string parsing to active negotiation using W3C User-Agent Client Hints (Sec-CH-UA). By capturing high-entropy client hints at the edge or via asynchronous JavaScript APIs and mapping them directly into the structured user-agent object in OpenRTB 2.6, publishers and SSPs preserve deterministic device targeting across all Chromium-based traffic.

How Chromium Froze the Legacy User-Agent String

Chromium executed its User-Agent Reduction initiative across a multi-stage rollout between Chrome 101 and Chrome 113 to curtail passive browser fingerprinting. Historically, the User-Agent HTTP header broadcasted high-entropy system attributes on every single network request, including subresource fetches to dozens of third-party domains, without the origin server ever requesting that data or the browser enforcing cross-origin permission controls. Ad tech endpoints, analytics collectors, and content delivery networks parsed this unstructured string using brittle lookup tables (such as WURFL or DeviceAtlas) to extract the operating system version, CPU architecture, build identifier, and hardware model.

Rather than deprecating the header outright and breaking millions of legacy web applications that rely on basic browser detection, the Chromium team opted to freeze the high-entropy segments of the string into static, backward-compatible placeholders. Under this architecture, the browser continues to send a syntactically valid User-Agent header, and navigator.userAgent, navigator.appVersion, and navigator.platform continue to return expected string formats, but the variable tokens inside those strings are locked to fixed historical constants.

At the same time, Chromium introduced the User-Agent Client Hints (UA-CH) framework, governed by the W3C Web Platform Incubator Community Group (WICG) and RFC 8942. UA-CH bifurcates device telemetry into two distinct tiers: low-entropy hints that are transmitted by default on every HTTPS request, and high-entropy hints that are withheld unless explicitly requested by the server via HTTP response headers or queried asynchronously through the browser's navigator.userAgentData JavaScript API.

Why Android Device Model and Minor OS Version Disappeared from navigator.userAgent

The impact of User-Agent Reduction is asymmetric across operating systems, hitting Android mobile web inventory far harder than desktop or iOS traffic. On Apple iOS and iPadOS devices, WebKit had already masked hardware model numbers inside the User-Agent string years earlier, collapsing all iPhones into a generic iPhone token. On Android, however, Chromium historically exposed the exact OEM model string (Build.MODEL) and firmware build number directly inside the parenthetical system comment of the User-Agent header.

With the completion of Phase 6 of Chromium's reduction schedule in Chrome 110, three critical tokens inside the Android mobile User-Agent string were permanently frozen:

  1. The Operating System Version Token: Regardless of whether the underlying handset runs Android 11, 12, 13, or 14, the legacy string always reports Android 10.
  2. The Hardware Model and Build Token: The specific device identifier (for example, Pixel 8 Pro or SM-S918U) and the Android build tag are replaced by the static character K.
  3. The Minor Browser Version Token: The four-part Chromium version number (such as 124.0.6367.82) is truncated so that the minor, build, and patch numbers are zeroed out, leaving only the major milestone release (124.0.0.0).

Because navigator.userAgent in the browser DOM reads from the exact same reduced string generator as the outbound HTTP header, client-side header bidding adapters running in Prebid.js cannot recover the device model simply by inspecting legacy DOM properties. Unless the publisher page or the programmatic wrapper actively queries the User-Agent Client Hints surface, every Android Chrome impression enters the auction pipeline stripped of its hardware identity.

Requesting High-Entropy Client Hints via Accept-CH and Permissions-Policy Headers

By default, Chromium browsers send only three low-entropy client hint headers on standard HTTPS requests: Sec-CH-UA (containing the browser brand list and significant/major version), Sec-CH-UA-Mobile (a boolean ?1 or ?0 flag indicating a mobile viewport), and Sec-CH-UA-Platform (reporting the high-level operating system name, such as "Android" or "Windows"). These default hints are insufficient for granular programmatic targeting or anti-fraud verification.

Before-and-after HTTP header inspection comparing a frozen Android 10 Model K User-Agent string against an Accept-CH Client Hints handshake unlocking Android 15.0.0 Pixel 9 Pro metadata in the OpenRTB device.sua object.

To unlock the underlying hardware and OS metadata, a publisher's web server or CDN edge must opt into high-entropy hints during the initial document response by sending the Accept-CH HTTP header. The critical high-entropy tokens required for OpenRTB 2.6 device resolution include:

  • Sec-CH-UA-Model: Returns the exact hardware model string (for example, "SM-S928B").
  • Sec-CH-UA-Platform-Version: Returns the full semantic version of the operating system (for example, "14.0.0").
  • Sec-CH-UA-Full-Version-List: Returns the complete four-part version string for each brand in the browser's compatibility list, which is crucial for invalid traffic (IVT) scanners that detect spoofed or outdated headless Chromium builds.
  • Sec-CH-UA-Arch and Sec-CH-UA-Bitness: Report the underlying CPU architecture ("arm" or "x86") and bitness ("64" or "32").

However, opting into Accept-CH on the publisher's first-party origin only instructs the browser to attach those high-entropy headers on subsequent requests back to that same first-party origin. Because Prebid Server endpoints, SSP ad tags, and Google Publisher Tag (GPT) scripts execute cross-origin requests to third-party domains, the browser will strip high-entropy Sec-CH-UA-* headers from outbound bid requests unless the publisher explicitly delegates permission.

Cross-origin delegation is enforced at the document level using the Permissions-Policy HTTP response header (or an equivalent <meta http-equiv="delegate-ch"> tag in the HTML <head> when publishers cannot modify CDN response headers). Our device intelligence infrastructure ingests both HTTP-delegated client hints and JavaScript-extracted telemetry to ensure zero signal loss across server-to-server and client-side auction paths. Below is a production NGINX / CDN header configuration that requests high-entropy hints and delegates them to participating programmatic endpoints:

Accept-CH: Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Wow64
Permissions-Policy: ch-ua-model=(self "https://hb.skyboosts.com" "https://securepubads.g.doubleclick.net"), ch-ua-platform-version=(self "https://hb.skyboosts.com" "https://securepubads.g.doubleclick.net"), ch-ua-full-version-list=(self "https://hb.skyboosts.com" "https://securepubads.g.doubleclick.net"), ch-ua-arch=(self "https://hb.skyboosts.com"), ch-ua-bitness=(self "https://hb.skyboosts.com")
Vary: Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Mobile

For publishers that cannot modify HTTP response headers at the origin or CDN layer, placing the delegate-ch meta tag early in the document <head> before any Prebid.js or GPT script tags execute achieves the same cross-origin delegation for all subsequent subresource fetches on the initial page load.

Populating the OpenRTB 2.6 device.sua (Structured User-Agent) Object

Once high-entropy client hints are unlocked via HTTP delegation or retrieved in the browser via navigator.userAgentData.getHighEntropyValues(), the telemetry must be serialized into a standardized format that downstream DSPs can ingest without rebuilding custom string parsers. The IAB Tech Lab addressed this directly: the OpenRTB 2.6 specification defines the device.sua (Structured User-Agent) object to carry high-entropy W3C User-Agent Client Hints (browsers, platform, mobile, architecture, bitness, and model) after Chromium froze legacy User-Agent strings.

Unlike the flat device.ua string, device.sua is a strongly typed JSON object designed to mirror the exact data structures returned by the W3C NavigatorUAData interface and Sec-CH-UA-* HTTP headers. Both browsers and platform utilize the OpenRTB BrandVersion sub-object, which separates the software brand name from an array of version hierarchy strings (breaking a version like "124.0.6367.82" into ["124", "0", "6367", "82"]). Additionally, device.sua includes a source integer field that tells the buying platform exactly how the SSP derived the data:

  • 0: Unknown source.
  • 1: Derived strictly from low-entropy client hints (Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform) or the synchronous navigator.userAgentData properties.
  • 2: Derived from high-entropy client hints (Accept-CH headers or the asynchronous getHighEntropyValues() promise).
  • 3: Synthesized by parsing a legacy User-Agent string (not recommended for Chromium M110+ traffic).

In Prebid.js 11.x, the core auction controller automatically queries navigator.userAgentData.getHighEntropyValues() during initialization and populates ortb2.device.sua in the first-party data object before bidder adapters construct their payloads. However, if a publisher loads Prebid.js asynchronously with an aggressive auction timeout curve, or uses a custom server-side wrapper, explicitly resolving the promise prior to pbjs.requestBids() prevents race conditions where the first impression on a session fires with only source: 1 low-entropy hints.

// Explicitly resolving high-entropy Client Hints for Prebid.js 11.x ortb2 injection
async function enrichOrtb2WithSua() {
  if (!navigator.userAgentData || typeof navigator.userAgentData.getHighEntropyValues !== 'function') {
    return;
  }

  try {
    const hints = await navigator.userAgentData.getHighEntropyValues([
      'architecture',
      'bitness',
      'model',
      'platformVersion',
      'fullVersionList',
      'wow64'
    ]);

    const suaPayload = {
      source: 2,
      mobile: navigator.userAgentData.mobile ? 1 : 0,
      architecture: hints.architecture || '',
      bitness: hints.bitness || '',
      model: hints.model || '',
      platform: {
        brand: navigator.userAgentData.platform || '',
        version: hints.platformVersion ? hints.platformVersion.split('.') : []
      },
      browsers: (hints.fullVersionList || navigator.userAgentData.brands || []).map(b => ({
        brand: b.brand,
        version: b.version ? b.version.split('.') : []
      }))
    };

    window.pbjs = window.pbjs || { que: [] };
    window.pbjs.que.push(function() {
      window.pbjs.mergeConfig({
        ortb2: {
          device: {
            sua: suaPayload
          }
        }
      });
    });
  } catch (err) {
    // Fallback handled gracefully by default low-entropy navigator.userAgentData
  }
}

When Prebid Server or our SSP exchange serializes this data into an outbound OpenRTB 2.6 bid request, we populate device.sua alongside the legacy device.ua string. Maintaining both fields ensures full backward compatibility while giving modern DSP bidders access to the unredacted hardware profile:

{
  "id": "80ce30c53c16e",
  "imp": [
    {
      "id": "1",
      "banner": { "w": 300, "h": 250 },
      "bidfloor": 1.45,
      "bidfloorcur": "USD"
    }
  ],
  "device": {
    "ua": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36",
    "ip": "172.56.21.89",
    "make": "Samsung",
    "model": "SM-S928U",
    "os": "Android",
    "osv": "14.0.0",
    "sua": {
      "browsers": [
        { "brand": "Chromium", "version": ["124", "0", "6367", "82"] },
        { "brand": "Google Chrome", "version": ["124", "0", "6367", "82"] },
        { "brand": "Not-A.Brand", "version": ["99", "0", "0", "0"] }
      ],
      "platform": {
        "brand": "Android",
        "version": ["14", "0", "0"]
      },
      "mobile": 1,
      "architecture": "arm",
      "bitness": "64",
      "model": "SM-S928U",
      "source": 2
    }
  }
}

Notice how our exchange pipeline also backfills the top-level device.model ("SM-S928U") and device.osv ("14.0.0") attributes using the verified device.sua payload. While native mobile app developers utilizing our cross-platform SDKs have direct OS-level access to hardware identifiers via Android's android.os.Build APIs, mobile web publishers depend entirely on this device.sua synthesis to prevent their top-level OpenRTB device fields from defaulting to "K" and "10".

Impact on Mobile Web eCPMs When Device Model Is Missing

The financial penalty of failing to capture and forward Sec-CH-UA telemetry on Android mobile web inventory is immediate and measurable across both open exchange and curated deal pipes. While Apple's iOS ecosystem exhibits relatively narrow hardware fragmentation, the global Android ecosystem spans thousands of distinct OEM models ranging from $40 ultra-budget devices in emerging markets to $1,800 foldable flagships. Because mobile web traffic on Chrome lacks a persistent hardware advertising ID (GAID is accessible only within native apps), DSP machine learning models rely heavily on contextual and hardware signals to predict conversion probability and user lifetime value.

When a bid stream sends device.ua containing Android 10; K without a populated device.sua object (source: 2), buy-side valuation breaks down across four distinct vectors:

  1. Loss of Hardware Price-Tier Segmentation: Luxury retail, automotive, telecom, and financial services campaigns routinely target high-end Android handsets (such as recent Samsung Galaxy S/Z series or Google Pixel Pro models) as a deterministic proxy for household income. When the model field collapses into "K", these campaigns exclude the impression at the pre-bid filtering stage, removing high-CPM demand from the auction and reducing overall bid density.
  2. OS Version Targeting Mismatches: App install and performance advertisers running mobile web-to-app acquisition campaigns frequently require Android 12, 13, or 14 to support modern deep-linking behaviors, passkey authentication, or specific graphics APIs. If the SSP reports device.osv: "10" parsed from the frozen legacy User-Agent string, every user on Android 11 through 14 is misclassified as running a four-year-old operating system and filtered out of those line items.
  3. Aggressive Bid Shading on Unverified Hardware: Even when DSPs bid on unidentified Android inventory, their valuation models group model: "K" traffic into a blended residual bucket. Because that bucket mixes premium devices with low-end hardware that suffers from slow page rendering and lower viewability rates, the DSP's clearing price algorithms shade bids downward by 18% to 35% compared to requests with verified device.sua model strings.
  4. Pre-Bid IVT False Positives: Verification vendors such as HUMAN and Pixalate analyze browser build numbers (Sec-CH-UA-Full-Version-List) alongside hardware architecture (Sec-CH-UA-Arch) to distinguish genuine mobile handsets from spoofed data-center emulators running x86 Linux kernels. Providing source: 2 high-entropy client hints confirms that the browser passed active TLS and JavaScript negotiation, lowering false-positive IVT blocks and increasing clean monetizable fill.

To audit your current mobile web pipeline, inspect outbound bid requests from an Android Chrome device in Chrome DevTools or via your SSP log-level data feeds. Verify that your origin server returns Accept-CH and Permissions-Policy headers on document responses, confirm that Prebid.js 11.x populates ortb2.device.sua with source: 2 prior to auction initiation, and ensure your SSP exchange maps device.sua.model and device.sua.platform.version into both the OpenRTB 2.6 sua object and legacy top-level device fields for maximum DSP compatibility.

Next step

Put it to work.

Every pattern in this article runs on our unified SSP and monetization platform: GAM and MCM management, Prebid client and server-side, and CTV SSAI.

Net-30 / Net-60 terms · 100% brand-safe demand