Understanding User-Agent Strings and Browser Headers

Technical Dive
Jul 19
by Idan Raman
User-Agent Strings & Headers

Every request a browser makes carries a user-agent string and a bundle of headers that ride alongside it—Accept, Accept-Language, and a growing set of structured client hints. None of it is secret, and none of it is optional: it's how a server decides what markup to send, which polyfills to skip, and, increasingly, whether the request looks like it came from the browser it claims to. For automation, the practical problem isn't that these headers exist. It's that a script which gets them almost right is often worse off than one that never touched them at all.

What's Actually Inside a User-Agent String

A modern Chrome UA string—Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36—reads like an accumulated history of the web rather than a clean spec. The leading Mozilla/5.0 is a compatibility token dating back to the browser wars; AppleWebKit and Safari are there because Chrome's engine descends from WebKit and sites once sniffed for it; the actual browser and version only show up at the end. Servers still parse this string for content negotiation and analytics, and a browser automation framework that changes it without changing anything else is editing one line of a document that's supposed to read as a whole.

The Headers That Travel With It

The UA string hasn't been the only identity signal for a while. Chromium now sends User-Agent Client Hints—Sec-CH-UA, Sec-CH-UA-Platform, Sec-CH-UA-Mobile—as structured, low-entropy alternatives to parsing a string, plus Sec-Fetch-Site, Sec-Fetch-Mode, and Sec-Fetch-Dest, which describe how and why a request was made rather than what sent it. A real browser keeps all of these in agreement: the UA string, the client hints, and the fetch metadata all describe the same engine, version, and platform. That agreement isn't enforced by one header field—it falls out of the fact that a genuine browser generated the whole request in one place.

Why a Changed String Alone Doesn't Fix Anything

This is where a lot of automation scripts run into trouble that looks like flakiness but is actually a coherence problem. Overriding navigator.userAgent in a headless session changes one signal while leaving the client hints, the rendering quirks, and sometimes a stray HeadlessChrome token elsewhere in the stack untouched—so the request describes a browser that doesn't quite exist. Sites that branch on UA for legitimate reasons (serving a different layout to older browsers, gating a feature by platform) can end up serving the automation script markup or scripts it wasn't tested against, which shows up as a selector that stopped matching or a page that half-renders. The header bundle rewards internal consistency, not any single value in it.

How Anchor Keeps the Bundle Coherent

Anchor's sessions run a full, genuine Chromium build rather than a stripped-down headless binary with a patched UA string, so the user-agent, client hints, and fetch metadata a session sends are naturally consistent with each other because one real browser produced all of them. For workflows that also need to manage the harder fingerprinting signals underneath the header layer—WebGL, canvas, timing characteristics—Extra Stealth Mode extends that same coherence to the browser environment itself, and it's designed to run behind a proxy so the network origin lines up with the rest of the identity:

const session = await anchorClient.sessions.create({
  browser: {
    extra_stealth: { active: true },
    viewport: { width: 1440, height: 900 },
  },
  session: {
    proxy: { active: true },
  },
});

The goal isn't to hide what the session is—it's to make sure every signal it emits, from the UA string down to the TLS handshake, describes the same browser. That's what lets automation behave predictably against sites that were built, reasonably, to expect a real one.

If your scripts are fighting header mismatches instead of your actual automation logic, Anchor runs real browser sessions so the UA string, client hints, and fingerprint stay coherent by default. Check the Extra Stealth Mode docs and get an API key to try it on your next workflow.

Stay ahead in browser automation

We respect your inbox. Privacy policy

Welcome aboard! Thanks for signing up
Oops! Something went wrong while submitting the form.