What a page can read about your browser
Before your code clicks anything, a site has already read the TLS handshake, the header order and dozens of JavaScript properties, and compared them with each other and with your IP. This page maps those signals and what each browser on the fleet sends for each, as measured in our cases and comparisons.
The signals at a glance
| Signal | Read by | Set by | Measured in |
|---|---|---|---|
| TLS ClientHello (JA3/JA4) | The server, before any HTML | The browser's network stack | case 01 |
| HTTP/2 SETTINGS and frames | The server | The network stack | case 01, case 09 |
| Header order and values | The server | The browser, and anything your code changes | case 09 |
| User agent and client hints | Server and page | The browser build | Chrome vs Chromium, Chrome vs Brave |
navigator: webdriver, plugins, permissions, cores, memory, screen | Page scripts | Build, headless or headful, the host | case 16, case 05 |
| Canvas and WebGL | Page scripts | Build and GPU (software on the fleet) | case 16, case 49 |
| Installed fonts | Page scripts | The OS image, or the persona | case 52 |
| WebRTC candidates | Page scripts | Browser and network path | case 54 |
| Automation (CDP) side effects | Page scripts | Your automation client | case 43, case 63 |
| Timezone, locale, geolocation vs IP | Page and server together | Context options, or Camoufox's geoip | case 35, case 15 |
Before any JavaScript: TLS and HTTP/2
The first packet a server receives is the TLS ClientHello: cipher suites, extensions and their order. Anti-bot systems condense it into a JA3/JA4 fingerprint, and the HTTP/2 SETTINGS and WINDOW_UPDATE frames into an Akamai HTTP/2 fingerprint, then check both against the browser the user agent claims to be. Measured on tls.peet.ws (case 01, 2026-09-30):
- Each engine sends its own real fingerprint: JA4
t13d1517h2_…for Chrome,t13d1516h2_…for Edge (one extension fewer),t13d1617h2_…for Firefox. Nothing is patched. - Chrome and Edge share the HTTP/2 fingerprint; so do Chrome and Brave, while Brave's TLS drops an extension like Edge's (Chrome vs Brave, 2026-10-09).
- Camoufox's TLS is identical to Firefox's, whatever OS its persona claims, which is consistent: Firefox's TLS stack is the same on every OS.
- WebKit speaks HTTP/1.1 through a proxy, with 29 cipher suites (JA4
t13d2913h1_…): nothing like a browser on a real Mac. - These come from the browser, not your code or the proxy:
page.requestand HTTP clients outside the browser send their own, different fingerprint.
Header order and values
Browsers send request headers in a fixed order, and some checks compare that order with the real browser's. Measured with real Chrome on tls.peet.ws (case 09, 2026-09-30):
- Chrome puts
accept-languageafteraccept-encoding. BothlocaleandextraHTTPHeadersmove it up, right afteruser-agent, an order real Chrome doesn't send on a navigation. extraHTTPHeaderssends exactly what you wrote (de-DEinstead of Chrome'sde-DE,de;q=0.9).route.continue({ headers })reorders everything and is the most visible of the four methods.- TLS and HTTP/2 stay the same whichever method you use.
- Brave adds
sec-gpc: 1, a header Chrome never sends (Chrome vs Brave).
Navigator, client hints and the host
The user agent string is one of several places a browser names itself. Client hints (Sec-CH-UA headers and navigator.userAgentData) carry brand lists, and a handful of navigator properties give headless and automated browsers away in a millisecond.
| Signal | What we measured |
|---|---|
| Client-hint brands | Chrome sends Google Chrome and Chromium; Playwright's Chromium sends only Chromium, the easiest server-side way to tell them apart (Chrome vs Chromium, 2026-10-07). Edge sends Microsoft Edge (Chrome vs Edge). Brave's user agent is identical to Chrome's, but its hints say Brave with reduced versions (Chrome vs Brave). WebKit has no userAgentData at all (WebKit vs Chromium). |
HeadlessChrome in the user agent | Headless Chromium, Chrome and Patchright all say it; headful builds don't (case 05, case 16). |
navigator.webdriver | false on every Chromium build on the fleet, headless included, and on Camoufox. true on Playwright's Firefox and WebKit (case 16, Firefox vs Camoufox, WebKit vs Chromium). |
Plugins, permissions, window.chrome | Headless Chromium: 0 plugins, no window.chrome, and Notification.permission *denied* while permissions.query says *prompt*. Headful Chromium fixes all of it with the same build: 5 plugins, consistent permissions, window.chrome present (case 16, 2026-10-04). |
| Cores, memory, screen | Chromium builds report the server's own hardware (64 cores and 32 GB in case 16) and a 1280×720 screen: a desktop that doesn't exist. Camoufox reports a consumer machine; Brave farbles cores and screen per session (case 16, Chrome vs Brave). |
| Platform vs user agent | WebKit claims a Mac in its user agent while navigator.platform says Linux x86_64 (WebKit vs Chromium, 2026-10-18). |
Canvas, WebGL and audio
Scripts draw text and shapes off-screen and hash the pixels (canvas), read the GPU strings and rendering output (WebGL), or process a tone through the audio stack. The result depends on the engine, fonts and GPU, so it identifies a device without any cookie.
- WebGL is the tell that outlives headful: Chrome, Edge, Chromium and Patchright all report Google's
SwiftShadersoftware renderer, headless or headful, because the servers have no GPU (case 16, case 05). bot.sannysoft.com flags exactly that on headful Chrome and Patchright (case 63). Playwright's Firefox reportsllvmpipe(Firefox vs Camoufox). - Chrome, Edge and Chromium draw the same canvas hash: switching between them changes the name, not the drawing (Chrome vs Edge, Chrome vs Chromium).
- Brave farbles: a different canvas hash, cores and screen per session and per site (Chrome vs Brave).
- Camoufox reports a consumer GPU string and re-seeds its canvas every launch: the same drawing hashes the same twice within a session, but differently in each new session, even with a persistent profile that brings back the rest of the device (case 49, 2026-10-15).
- Audio: we haven't published an audio-fingerprint measurement yet; vendors do read it (case 05).
Installed fonts
A script measures the width of text in each candidate font against a fallback; a family that changes the width is installed. Measured with a 46-family probe on Camoufox personas (case 52, 2026-10-16):
- The font list follows the persona and is stable between launches: two Windows launches gave the same 20 families and the same hash, unlike the canvas.
- Each OS shows its own families: Segoe UI, Calibri and Consolas on Windows; Helvetica Neue, Menlo and Avenir on macOS; only three on Linux, and a short list is unusual in itself.
fontsadds only families the server has, and a width probe can't see the fallback face itself, so on the Windows persona it reports no Arial. A script that comparesserifwithmonospacecan notice that.
WebRTC
WebRTC gathers network candidates for peer connections, and a STUN request can reveal a public address other than the proxy's. Measured behind a sticky residential exit per setup (case 54, 2026-10-16):
- No setup leaked a second IP: a session that uses a proxy can't send UDP past it.
- Chromium, Chrome and Firefox keep WebRTC but expose only anonymous
.localmDNS names, like a real visitor on a network that blocks UDP. - Camoufox reports the proxy's exit as its public address, so WebRTC agrees with HTTP. Keep the exit sticky: the address is looked up at launch.
block_webrtc: trueremoves the API entirely, which is itself unusual for a desktop browser.
Automation side effects (CDP)
An automation client changes how the page behaves: console messages are serialised for the client, and a page can time that. Measured with six setups (case 43, 2026-10-13):
- The old
Error.stackgetter trick fired in none of the six setups. TheError.messagegetter fires on every Chromium build whether or not a client listens, so it says "Chromium", not "automated". - Logging a big object costs 3.1–4.8× more on Chrome and Playwright's Chromium than on Patchright (2.6–2.8×), because Playwright enables the runtime domain. It is a timing signal that sites can only use statistically.
- Public test pages look for the connection itself: fingerprint.com typed headful Chrome and Patchright as
chromium_automation, and deviceandbrowserinfo flagged Chrome for CDP automation in the page and in a worker (case 63, 2026-10-20).
Timezone, locale and geolocation vs the IP
A risk engine compares the browser's clock, languages and position with where its IP is. Defaults that ignore the exit are the cheapest mismatch to spot.
- Playwright's Chromium and Patchright default to
UTCanden-USwhatever the exit: a German IP with a UTC clock, and geolocation refused. SettingtimezoneId,localeandgeolocation(plus the permission) on the context makes them agree (case 35, 2026-10-11). - Camoufox's
geoipsets timezone, locale and geolocation from the exit at launch (Camoufox vs Patchright); read them withmain_world_eval, because a defaultpage.evaluateruns in an isolated world that reportsUTC(case 15). - A rotating proxy can split the story: the timezone is set at launch and the next request may exit in another country. Pick the exit country to match the locale and use a sticky exit (case 04, case 49).
What each fleet browser changes
The same signals, by browser. "Real" means the browser sends its own values, unpatched.
| Browser | Network (TLS, HTTP/2, headers) | Identity (UA, client hints) | Device (GPU, canvas, screen, fonts) | Automation tells |
|---|---|---|---|---|
| Chrome, Edge | Real, per browser; Edge's JA4 differs by one extension | Real brands (Google Chrome, Microsoft Edge) | The server's: SwiftShader, its cores, one canvas hash | HeadlessChrome when headless; CDP side effects |
| Chromium | Real Chromium; one extension fewer than Chrome | Chromium only, no Google brand | Same as Chrome | Headless tells when headless; CDP side effects |
| Brave | Brave's own JA4, sec-gpc: 1 | Chrome's user agent, Brave in the hints | Farbled per session and site | Same as Chrome headful on sannysoft |
| Patchright | Real Chromium | Chromium | Same as Chromium | No console events, cheapest CDP cost; still typed as automation by one test page |
| Camoufox | Real Firefox (the same on every OS) | Firefox on a generated desktop OS | A consumer persona; canvas re-seeded per launch, fonts stable | Fewest flags on the test pages; webdriver false |
| Firefox | Real Firefox | Firefox on Linux | The server's: llvmpipe, its cores | webdriver true |
| WebKit | HTTP/1.1 through a proxy, 29 ciphers | A Mac UA on a Linux platform, no client hints | The server's | webdriver true |
Camoufox and Firefox are based on Firefox source; cdpfleet is not affiliated with Mozilla. Sources: case 01, case 16, case 63 and the comparisons linked above.
What public bot-test pages concluded
Four public test pages against five fleet browsers on 2026-10-20 (case 63): headless Chromium was flagged by every page; headful Chrome and Patchright passed bot.sannysoft.com except its WebGL renderer check but were still typed as automation by fingerprint.com; WebKit was caught on navigator.webdriver; Camoufox drew the fewest flags, with verdicts that varied from load to load. Which protection vendor a site runs, and the signals each one leaves, is in case 62.