Skip to content
Chrome 156 → 157

Chrome 157: what changed for fingerprinting and automation

Chrome 157 changes what a page can observe in 18 places, measured on the stable builds of both versions. A server sees the same TLS handshake, HTTP/2 settings and header order as on 156 in a fresh profile; only the version strings and the GREASE brand change. Scripts see much more: four new global interfaces for HTML-in-canvas and Trusted Types, over forty new HTML insertion methods, thirteen Protected Audience members gone from navigator, and text inputs that measure differently.

Pim
Pim · Clearcote Research
Stable release
November 3, 2026
Measured on (Linux x64)
156.0.8078.73 → 157.0.8103.50
Source read at
156.0.8078.99 → 157.0.8103.77
V8
15.6.75.54 → 15.7.112.40

Key takeaways

  • The biggest API change in months: HTML-in-canvas ships (drawElementImage(), ElementImage, CanvasPaintEvent, ElementGeometryUpdateEvent), and Element gains appendHTML(), streamHTML() and 20 more insertion methods, with TrustedHTMLParserOptions beside them.
  • Protected Audience leaves the web platform: navigator.joinAdInterestGroup, runAdAuction and eleven more members are gone on https:// pages, so Navigator.prototype drops from 81 to 68 own properties.
  • Layout and style values move: text inputs leave their inline padding out of clientWidth and clientLeft, an inline element with appearance: auto stays inline, position-visibility computes to anchor-visible, and text-decoration-inset adds a computed-style property.
  • V8 15.7 fixes are visible from one line each: await in a static field, a conditional after an arrow function, [\q{a}] with vi, setFromHex on bad input, and era-only Temporal formatting.
  • A server sees the same JA4, JA3N and HTTP/2 fingerprint as on 152 to 156. The new GREASE brand is Not-A.Brand version 99, listed first, then Google Chrome, then Chromium.

Every check, both versions, and your browser

29 one-line checks across 18 changes. The strip shows where they are; the table shows what each returns on Chrome 156 and Chrome 157. Run them in this browser to see which version its engine matches.

Chrome 156 → Chrome 157 · 29 checks

Run the checks to see which Chrome version this browser's engine matches.

Where the changes are. Select a surface to filter the rows.

CheckChrome 156Chrome 157This browser
Client Hints
New GREASE brand and brand order in User-Agent Client Hintsnavigator.userAgentData.brands.map((b) => `${b.brand};v=${b.version}`).join(', ')"Not:A-Brand;v=8, Chromium;v=156, Google Chrome;v=156""Not-A.Brand;v=99, Google Chrome;v=157, Chromium;v=157"–
JavaScript and DOM
Protected Audience (interest-group and ad-auction) methods removed from navigatortypeof navigator.joinAdInterestGroup"function""undefined"n/a
Protected Audience (interest-group and ad-auction) methods removed from navigator'runAdAuction' in navigatortruefalsen/a
HTML-in-canvas: drawElementImage, ElementImage and canvas paint eventstypeof CanvasRenderingContext2D.prototype.drawElementImage"undefined""function"–
HTML-in-canvas: drawElementImage, ElementImage and canvas paint eventstypeof ElementImage"undefined""function"–
HTML-in-canvas: drawElementImage, ElementImage and canvas paint events'onpaint' in HTMLCanvasElement.prototypefalsetrue–
New HTML insertion and streaming methods, and Trusted Types parser optionstypeof Element.prototype.appendHTML"undefined""function"–
New HTML insertion and streaming methods, and Trusted Types parser optionstypeof Element.prototype.streamHTML"undefined""function"–
New HTML insertion and streaming methods, and Trusted Types parser optionstypeof TrustedTypePolicy.prototype.createParserOptions"undefined""function"–
window's property order moves againObject.getOwnPropertyNames(window).indexOf('ProgressEvent') > Object.getOwnPropertyNames(window).indexOf('Lock')falsetrue–
window's property order moves againObject.getOwnPropertyNames(PerformancePaintTiming.prototype)[0]"toJSON""paintTime"–
srgb-linear and display-p3-linear color spaces(() => { try { return new ImageData(1, 1, { colorSpace: 'srgb-linear' }).colorSpace; } catch (e) { return e.name; } })()"TypeError""srgb-linear"–
WebTransport request headers, responseHeaders and reliability'responseHeaders' in (globalThis.WebTransport?.prototype ?? {})falsetrue–
WebTransport request headers, responseHeaders and reliabilitytypeof globalThis.WebTransport?.supportsReliableOnly"undefined""boolean"–
Sanitizer setters return booleanstypeof new Sanitizer().removeUnsafe()"undefined""boolean"–
HTML parser ignores a head tag inside noscriptnew DOMParser().parseFromString('<head><noscript><head><style></style>', 'text/html').querySelector('noscript').childElementCount01–
Uint8Array.setFromHex writes the valid prefix before throwing(() => { const u = new Uint8Array(16); try { u.setFromHex('aa'.repeat(4) + 'zz' + 'aa'.repeat(11)); } catch (e) {} return u[0]; })()0170–
RegExp: class strings fold case under the v and i flagsnew RegExp('[\\q{a}]', 'vi').test('A')falsetrue–
JavaScript parser: await in static fields, conditional chains after arrow functions(() => { try { return (0, eval)('(function () { var await = 7; return class { static x = await; }; })().x'); } catch (e) { return e.name; } })()"SyntaxError"7n/a
JavaScript parser: await in static fields, conditional chains after arrow functions(() => { try { (0, eval)('0 ? 0 : () => {} ? 1 : 2'); return 'parsed'; } catch (e) { return e.name; } })()"parsed""SyntaxError"n/a
Parsing and rendering
Text inputs measure clientWidth and clientLeft from the content box(() => { const i = document.body.appendChild(document.createElement('input')); i.style.cssText = 'appearance: none; box-sizing: content-box; width: 100px; border: 5px solid; padding: 0 20px'; const v = `${i.clientWidth},${i.clientLeft}`; i.remove(); return v; })()"140,5""100,25"–
CSS
CSS text-decoration-insetCSS.supports('text-decoration-inset', '1px')falsetrue–
CSS text-decoration-insetgetComputedStyle(document.documentElement).length479480n/a
CSS text-overflow with a string valueCSS.supports('text-overflow', '"x"')falsetrue–
position-visibility: anchor-visible and anchor-validCSS.supports('position-visibility', 'anchor-visible')falsetrue–
position-visibility: anchor-visible and anchor-validCSS.supports('position-visibility', 'anchor-valid')falsetrue–
position-visibility: anchor-visible and anchor-valid(() => { const d = document.createElement('div'); d.style.positionVisibility = 'anchors-visible'; return d.style.positionVisibility; })()"anchors-visible""anchor-visible"–
appearance no longer changes display on non-widget elements(() => { const s = document.body.appendChild(document.createElement('span')); s.style.appearance = 'auto'; const v = getComputedStyle(s).display; s.remove(); return v; })()"inline-block""inline"–
Intl and time zones
Temporal toLocaleString with only era keeps the date fieldsTemporal.PlainDate.from('2026-11-03').toLocaleString('en-US', { era: 'short' }).includes('2026')falsetrue–

A tinted cell differs from the version before it. Values are as the DevTools console prints them. Checks that need a debugger attached are not run live.

TLS, HTTP/2 and request headers in Chrome 157

TLS ClientHello
Identical to Chrome 156 in a fresh profile: JA4 t13d1517h2_8daaf6152771_cb7bf5808d99 and JA3N bd4930bd9b000ee684830e44bab76fdf, measured three times on each Linux build against two independent TLS echo services, and five times on Windows 157.0.8103.40. Chrome's TLS client files are identical at the two tags. The root store goes from version 40 to 42 (one constraint date changes and one intermediate is added); the trust-anchor IDs Chrome advertises are unchanged.
HTTP/2
Identical to Chrome 156: Akamai fingerprint 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p, the same HEADERS priority and pseudo-header order m,a,s,p. In the source, spdy_session.cc only changes how a draining session is torn down.
Request headers
The same names in the same order on all 21 request types we measured, and the same values apart from the version: User-Agent, Sec-CH-UA (new GREASE brand, first), Sec-CH-UA-Full-Version and Sec-CH-UA-Full-Version-List. Read in the source but not measured: Chrome no longer sends the Intervention request header on scripts blocked by the document.write intervention, an author-set Intervention header now triggers a CORS preflight, and Background Fetch requests follow CORS.

Compare your own browser's handshake with these in the JA4 fingerprint checker.

Chrome 157 User-Agent strings and Client Hints

PlatformUser-Agent
WindowsMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/157.0.0.0 Safari/537.36
macOSMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/157.0.0.0 Safari/537.36
LinuxMozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/157.0.0.0 Safari/537.36
AndroidMozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/157.0.0.0 Mobile Safari/537.36
Headless (Linux)Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/157.0.0.0 Safari/537.36

The Linux and headless strings were measured on 157.0.8103.50; the string does not change within a major. The others follow Chromium's reduced User-Agent format (user_agent_utils.cc): only the major version is real, the rest is frozen, and the platform part is fixed per operating system.

Sec-CH-UA (the low-entropy brand list sent with every request):

"Not-A.Brand";v="99", "Google Chrome";v="157", "Chromium";v="157"

The Chrome user agent page has these for the current version and every recent one; what is my user agent shows your own.

User-Agent and Client Hints

high impactMeasured on both builds

New GREASE brand and brand order in User-Agent Client Hints

The fake GREASE brand and the order of the brand list are computed from the major version. On 157 the brand is Not-A.Brand version 99, listed first, then Google Chrome, then Chromium. Sent on every request in Sec-CH-UA and returned by navigator.userAgentData.brands in windows and workers.

navigator.userAgentData.brands.map((b) => `${b.brand};v=${b.version}`).join(', ')
Chrome 156
"Not:A-Brand;v=8, Chromium;v=156, Google Chrome;v=156"
Chrome 157
"Not-A.Brand;v=99, Google Chrome;v=157, Chromium;v=157"

Why it matters: A version number changed by hand, with the previous brand list left in place, produces a combination no real Chrome sends. The GREASE helper was simplified in 157 (one function inlined), with the same output for every major.

JavaScript and Web APIs

high impactMeasured on both builds

HTML-in-canvas: drawElementImage, ElementImage and canvas paint events

The CanvasDrawElement feature ships. New: drawElementImage() on CanvasRenderingContext2D and OffscreenCanvasRenderingContext2D, WebGL texElementSubImage2D(), GPUQueue.drawElementImageToTexture(), the ElementImage, CanvasPaintEvent and ElementGeometryUpdateEvent interfaces, and on HTMLCanvasElement requestPaint(), captureElementImage(), updateElementGeometry(), onpaint and more. The OffscreenCanvas and WebGL parts are also in dedicated, shared and service workers.

typeof CanvasRenderingContext2D.prototype.drawElementImage
Chrome 156
"undefined"
Chrome 157
"function"
  • typeof ElementImage"undefined"→ "function"
  • 'onpaint' in HTMLCanvasElement.prototypefalse→ true

Why it matters: Three new global interfaces in windows (one in workers) and about twenty new members, all synchronous to probe. It was an origin trial before, so a 156 page that registered a trial token can also see it.

high impactMeasured on both builds

New HTML insertion and streaming methods, and Trusted Types parser options

NewHTMLSettingMethods and TrustedTypesCreateParserOptions return to stable. Element gains appendHTML, prependHTML, beforeHTML, afterHTML, replaceWithHTML and their Unsafe and stream* variants (22 methods); ShadowRoot and CharacterData gain their share. TrustedTypePolicy.createParserOptions() and the TrustedHTMLParserOptions interface are new.

typeof Element.prototype.appendHTML
Chrome 156
"undefined"
Chrome 157
"function"
  • typeof Element.prototype.streamHTML"undefined"→ "function"
  • typeof TrustedTypePolicy.prototype.createParserOptions"undefined"→ "function"

Why it matters: Over forty new members on very common prototypes, visible to any enumeration of Element.prototype. The methods shipped once before and were moved back to experimental for 154, so 154 to 156 lack them.

medium impactMeasured on both builds

window's property order moves again

Apart from the four new globals, three existing ones move within Object.getOwnPropertyNames(window): ProgressEvent drops from near the start (position 315 on the page we measured) to after Lock and LockManager (1191), which themselves move up. On several prototypes the order of existing members changes too: paintTime and presentationTime now come first on the paint-timing interfaces, and pseudoTarget comes before constructor on UIEvent, AnimationEvent and TransitionEvent. Workers see the same ProgressEvent and Lock move.

Object.getOwnPropertyNames(window).indexOf('ProgressEvent') > Object.getOwnPropertyNames(window).indexOf('Lock')
Chrome 156
false
Chrome 157
true
  • Object.getOwnPropertyNames(PerformancePaintTiming.prototype)[0]"toJSON"→ "paintTime"

Why it matters: Any hash of the global or prototype property order changes, even where no name is added or removed. These are the kind of moves a hand-edited browser keeps from its older version.

low impactMeasured on both builds

WebTransport request headers, responseHeaders and reliability

WebTransport gains responseHeaders, reliability and the static supportsReliableOnly, and the constructor reads a headers option. Windows and all worker types; secure contexts only.

'responseHeaders' in (globalThis.WebTransport?.prototype ?? {})
Chrome 156
false
Chrome 157
true
  • typeof globalThis.WebTransport?.supportsReliableOnly"undefined"→ "boolean"

Why it matters: New members on an existing interface. Chrome Platform Status listed the headers for 156, but they ship in 157.

low impactMeasured on both builds

Uint8Array.setFromHex writes the valid prefix before throwing

When setFromHex() hits an invalid character it must still write the bytes decoded before it. In 156 the SIMD path (32 or more hex characters, non-shared buffer) dropped the whole 32-character batch that contained the error.

(() => { const u = new Uint8Array(16); try { u.setFromHex('aa'.repeat(4) + 'zz' + 'aa'.repeat(11)); } catch (e) {} return u[0]; })()
Chrome 156
0
Chrome 157
170

Why it matters: An engine-version tell from V8 15.7 that does not depend on the page; both the x86 (SSE) and ARM (Neon) paths were fixed.

low impactMeasured on both builds

JavaScript parser: await in static fields, conditional chains after arrow functions

Two grammar fixes in V8 15.7: await is a valid identifier in a static class field initializer of a sloppy script (156 threw a SyntaxError), and a ? b : () => {} ? c : d is now a SyntaxError (156 accepted it).

(() => { try { return (0, eval)('(function () { var await = 7; return class { static x = await; }; })().x'); } catch (e) { return e.name; } })()
Chrome 156
"SyntaxError"
Chrome 157
7
  • (() => { try { (0, eval)('0 ? 0 : () => {} ? 1 : 2'); return 'parsed'; } catch (e) { return e.name; } })()"parsed"→ "SyntaxError"

Why it matters: Parser behaviour is fixed per engine build and needs no permission.

HTML

low impactMeasured on both builds

HTML parser ignores a head tag inside noscript

With scripting disabled (for example in DOMParser documents), a <head> start tag in the "in head noscript" insertion mode is now ignored, as the HTML standard says. On 156 it closed the <noscript>, so the following elements moved out of it.

new DOMParser().parseFromString('<head><noscript><head><style></style>', 'text/html').querySelector('noscript').childElementCount
Chrome 156
0
Chrome 157
1

Why it matters: Parser output differences are deterministic and cheap to test.

Removed or deprecated

high impactMeasured on both builds

Protected Audience (interest-group and ad-auction) methods removed from navigator

The Fledge and AdInterestGroupAPI runtime features lose their stable status, so navigator.joinAdInterestGroup, leaveAdInterestGroup, runAdAuction, updateAdInterestGroups, createAuctionNonce, adAuctionComponents, getInterestGroupAdAuctionData, protectedAudience and the related members (13 in all) disappear from Navigator. They were secure-context only, so on plain-HTTP pages they were already absent.

typeof navigator.joinAdInterestGroup
Chrome 156
"function"
Chrome 157
"undefined"
  • 'runAdAuction' in navigatortrue→ false

Why it matters: The largest change to the window interface list in this release: thirteen members leave Navigator. A browser that claims 157 and still has them, or claims 156 and lacks them on a fresh profile, is inconsistent. HTTPS pages only.

Parsing and rendering behaviour

high impactMeasured on both builds

Text inputs measure clientWidth and clientLeft from the content box

For <input> text fields, clientLeft, clientWidth and scrollWidth now leave out the inline padding (the block axis keeps it). A default input loses its 2px left and right padding from clientWidth.

(() => { const i = document.body.appendChild(document.createElement('input')); i.style.cssText = 'appearance: none; box-sizing: content-box; width: 100px; border: 5px solid; padding: 0 20px'; const v = `${i.clientWidth},${i.clientLeft}`; i.remove(); return v; })()
Chrome 156
"140,5"
Chrome 157
"100,25"

Why it matters: Element geometry is a common fingerprint input, and default form controls are often measured. The 157 numbers are the ones the WPT test added with the change asserts.

CSS

medium impactMeasured on both builds

CSS text-decoration-inset

The text-decoration-inset property ships. It is the only addition to the stable CSS property list, so getComputedStyle() lists one more property: 479 to 480 on the page we measured (156 had already dropped text-decoration-skip-spaces, which is why 156 lists one fewer than 155).

CSS.supports('text-decoration-inset', '1px')
Chrome 156
false
Chrome 157
true
  • getComputedStyle(document.documentElement).length479→ 480

Why it matters: A new property name changes the computed-style length and the property list, which many fingerprints hash.

medium impactMeasured on both builds

position-visibility: anchor-visible and anchor-valid

The singular anchor-visible keyword and the new anchor-valid keyword ship for position-visibility. The plural anchors-visible still parses but now serializes as anchor-visible.

CSS.supports('position-visibility', 'anchor-visible')
Chrome 156
false
Chrome 157
true
  • CSS.supports('position-visibility', 'anchor-valid')false→ true
  • (() => { const d = document.createElement('div'); d.style.positionVisibility = 'anchors-visible'; return d.style.positionVisibility; })()"anchors-visible"→ "anchor-visible"

Why it matters: Three deterministic answers, including a serialization change for a value that existed before.

medium impactMeasured on both builds

appearance no longer changes display on non-widget elements

Elements without a native widget (span, a, div, li, summary) keep their specified display when appearance is set. On 156 an inline element with appearance: auto computed to inline-block.

(() => { const s = document.body.appendChild(document.createElement('span')); s.style.appearance = 'auto'; const v = getComputedStyle(s).display; s.remove(); return v; })()
Chrome 156
"inline-block"
Chrome 157
"inline"

Why it matters: Computed-style values of ordinary elements change, which affects style hashes and layout.

Intl, time zones and locale

low impactMeasured on both builds

Temporal toLocaleString with only era keeps the date fields

For Temporal objects, an options bag that sets only era no longer suppresses the default year, month and day.

Temporal.PlainDate.from('2026-11-03').toLocaleString('en-US', { era: 'short' }).includes('2026')
Chrome 156
false
Chrome 157
true

Why it matters: Deterministic in every realm and independent of the operating system.

What the docs claim that the source does not show

Release notes and Chrome Platform Status describe intent; the source at the release tag is what shipped. These entries differ, so a version check built on the docs alone would be wrong.

  • Chrome Platform Status (milestone 157, enabled by default): Long Animation Frames style and layout duration.

    LongAnimationFrameStyleDuration is still experimental (with an origin trial) at 157.0.8103.77, as at 156.

  • Chrome Platform Status (milestone 157, enabled by default): Web Bluetooth, make BluetoothRemoteGATTServer an EventTarget.

    At 157.0.8103.77 BluetoothRemoteGATTServer still does not inherit from EventTarget.

  • Chrome Platform Status (milestone 157, stepped rollout): disconnect WebSockets on back/forward cache entry.

    The DisconnectWebSocketOnBFCache runtime flag is already stable at 156.0.8078.99 and unchanged at 157; there is no 156-to-157 source difference.

  • Chrome Platform Status lists HTML-in-canvas as "In development".

    CanvasDrawElement is stable at 157.0.8103.77 and its interfaces are in the stable interface listing.

  • Chrome Platform Status (milestone 156, enabled by default): WebTransport headers and responseHeaders.

    Experimental at every 156 tag up to 156.0.8078.99; stable at 157.0.8103.77.

  • Chrome Platform Status lists no removal for milestone 157.

    Thirteen Protected Audience members of navigator leave the stable interface listing at 157.

Reproduce these results

  1. 1. Get both versions

    Use Google Chrome's own Linux packages of the two exact versions. Google keeps every version in its package pool:

    bash
    curl -sSO https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_156.0.8078.73-1_amd64.deb
    curl -sSO https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_157.0.8103.50-1_amd64.deb
    for f in google-chrome-*.deb; do dpkg -x "$f" "${f%.deb}"; done
  2. 2. Start each one clean

    A new profile directory per run and no automation flags. A fresh profile has not applied Google's field-trial configuration yet, which is the state these values describe. Use an https:// page: the Protected Audience and WebTransport members exist only in secure contexts.

    bash
    ./google-chrome-stable_157.0.8103.50-1_amd64/opt/google/chrome/chrome --user-data-dir="$(mktemp -d)" --no-first-run https://example.com
  3. 3. Run the checks

    Press "Run the checks in this browser" at the top of this page, or paste the script below into the DevTools console on any https:// page. Some checks are asynchronous; the script awaits them.

  4. 4. Check the network side

    Open tls.peet.ws or tls.browserleaks.com in each version and compare the JA4 and HTTP/2 fingerprints and the request headers with the values above. They are the same on 156 and 157 in a fresh profile.

  5. 5. Find a change in the source yourself

    Blink's test expectations record the page-visible surface of each release; the runtime feature list records what is on by default:

    bash
    git diff 156.0.8078.99 157.0.8103.77 -- third_party/blink/web_tests/virtual/stable/webexposed/
    git diff 156.0.8078.99 157.0.8103.77 -- third_party/blink/renderer/platform/runtime_enabled_features.json5

The whole check list as one script

Paste it into the DevTools console on Chrome 156 and on Chrome 157: it prints every check on this page with the value your browser returns.

chrome-157-probe.js
// Chrome 156 vs 157: paste into the DevTools console on any https:// page and press Enter.
// Each line prints "<change>  <check> = <value>". Expected values: clearcotelabs.com/chrome-releases/157
(async () => {
  const checks = [
    ["grease-brand", () => navigator.userAgentData.brands.map((b) => `${b.brand};v=${b.version}`).join(', ')],
    ["protected-audience-removed", () => typeof navigator.joinAdInterestGroup],
    ["protected-audience-removed", () => 'runAdAuction' in navigator],
    ["html-in-canvas", () => typeof CanvasRenderingContext2D.prototype.drawElementImage],
    ["html-in-canvas", () => typeof ElementImage],
    ["html-in-canvas", () => 'onpaint' in HTMLCanvasElement.prototype],
    ["html-insertion-methods", () => typeof Element.prototype.appendHTML],
    ["html-insertion-methods", () => typeof Element.prototype.streamHTML],
    ["html-insertion-methods", () => typeof TrustedTypePolicy.prototype.createParserOptions],
    ["window-property-order", () => Object.getOwnPropertyNames(window).indexOf('ProgressEvent') > Object.getOwnPropertyNames(window).indexOf('Lock')],
    ["window-property-order", () => Object.getOwnPropertyNames(PerformancePaintTiming.prototype)[0]],
    ["input-client-rect", () => (() => { const i = document.body.appendChild(document.createElement('input')); i.style.cssText = 'appearance: none; box-sizing: content-box; width: 100px; border: 5px solid; padding: 0 20px'; const v = `${i.clientWidth},${i.clientLeft}`; i.remove(); return v; })()],
    ["text-decoration-inset", () => CSS.supports('text-decoration-inset', '1px')],
    ["text-decoration-inset", () => getComputedStyle(document.documentElement).length],
    ["text-overflow-string", () => CSS.supports('text-overflow', '"x"')],
    ["position-visibility-values", () => CSS.supports('position-visibility', 'anchor-visible')],
    ["position-visibility-values", () => CSS.supports('position-visibility', 'anchor-valid')],
    ["position-visibility-values", () => (() => { const d = document.createElement('div'); d.style.positionVisibility = 'anchors-visible'; return d.style.positionVisibility; })()],
    ["appearance-display", () => (() => { const s = document.body.appendChild(document.createElement('span')); s.style.appearance = 'auto'; const v = getComputedStyle(s).display; s.remove(); return v; })()],
    ["linear-color-spaces", () => (() => { try { return new ImageData(1, 1, { colorSpace: 'srgb-linear' }).colorSpace; } catch (e) { return e.name; } })()],
    ["webtransport-headers-reliability", () => 'responseHeaders' in (globalThis.WebTransport?.prototype ?? {})],
    ["webtransport-headers-reliability", () => typeof globalThis.WebTransport?.supportsReliableOnly],
    ["sanitizer-boolean-setters", () => typeof new Sanitizer().removeUnsafe()],
    ["noscript-head-parser", () => new DOMParser().parseFromString('<head><noscript><head><style></style>', 'text/html').querySelector('noscript').childElementCount],
    ["setfromhex-prefix", () => (() => { const u = new Uint8Array(16); try { u.setFromHex('aa'.repeat(4) + 'zz' + 'aa'.repeat(11)); } catch (e) {} return u[0]; })()],
    ["temporal-era-defaults", () => Temporal.PlainDate.from('2026-11-03').toLocaleString('en-US', { era: 'short' }).includes('2026')],
    ["regexp-class-string-casefold", () => new RegExp('[\\q{a}]', 'vi').test('A')],
    ["parser-grammar-fixes", () => (() => { try { return (0, eval)('(function () { var await = 7; return class { static x = await; }; })().x'); } catch (e) { return e.name; } })()],
    ["parser-grammar-fixes", () => (() => { try { (0, eval)('0 ? 0 : () => {} ? 1 : 2'); return 'parsed'; } catch (e) { return e.name; } })()],
  ];
  const out = [];
  for (const [id, f] of checks) {
    let v;
    try { v = await f(); } catch (e) { v = "throws " + e.name; }
    const expr = f.toString().replace(/^\(\)\s*=>\s*/, "");
    out.push(id.padEnd(28) + expr + " = " + (typeof v === "string" ? JSON.stringify(v) : String(v)));
  }
  console.log(out.join("\n"));
})();

FAQ

When was Chrome 157 released?

Chrome 157 reached the stable channel on November 3, 2026. The current stable builds are 157.0.8103.51 on Windows and macOS and 157.0.8103.50 on Linux, the build measured on this page.

What is the Chrome 157 user agent string?

On Windows: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/157.0.0.0 Safari/537.36. Only the major version changes; the full version travels in Client Hints. The table above lists every platform.

What is the Sec-CH-UA header in Chrome 157?

"Not-A.Brand";v="99", "Google Chrome";v="157", "Chromium";v="157", measured on Linux and Windows. Both the fake GREASE brand and the order change with every major.

Did Chrome 157 change the TLS (JA3/JA4) or HTTP/2 fingerprint?

No: JA4 t13d1517h2_8daaf6152771_cb7bf5808d99 and the same HTTP/2 settings in a fresh profile, as on 152 to 156. See the network section.

How can a website tell Chrome 156 from Chrome 157?

The quickest lines are typeof Element.prototype.appendHTML, typeof ElementImage and CSS.supports('text-decoration-inset', '1px'). None of them needs a permission.

Why are the Protected Audience methods gone?

Chrome 157 turns the Fledge and AdInterestGroupAPI runtime features off by default, so the interest-group and ad-auction methods leave navigator. Google's release notes do not list it as a removal. Google's server-side field-trial configuration could already turn them off on 156, so an older profile may lack them on 156 too; the live column skips these checks for that reason.

Did Chrome 157 change anything for automation?

Not in what a page sees: navigator.webdriver and the headless User-Agent format are unchanged. In the DevTools protocol, read in the source: Debugger.Scope replaces empty with emptyReason, Page.setInputVisualization and CSS.forcePositionTryOption are new, Network.loadingFinished gains encodedBodyLength, and the --disable-reduce-accept-language switch is removed.

Clearcote runs Chromium 156, with all 240 patches carried forward.

Clearcote puts this into practice

An open-source Chromium with fingerprint control compiled into the engine. A drop-in for Playwright & Puppeteer.

  • 240engine patches
  • 35,915lines added
  • 345Chromium files changed
  • 365checks per build

Engine numbers are for the Pro build; the free open build is a smaller, public patch set.

Free for one browser: GitHub, a card check or a company Google account. Never charged.