Skip to content
Chrome 152 → 153

Chrome 153: what changed for fingerprinting and automation

Chrome 153 changed what a web page can observe in 18 places and nothing in the network handshake. Each change below has a one-line check, its value on both versions, and a link into Chromium's source. Run the checks in your own browser to see which version it matches.

Pim
Pim · Clearcote Research
Stable release
September 8, 2026
Measured on (Linux x64)
152.0.7977.82 → 153.0.8010.52
Source read at
152.0.7977.85 → 153.0.8010.55
V8
15.2.124.21 → 15.3.76.13

Key takeaways

  • A page can tell 152 from 153 in one line: typeof Iterator.zip is "undefined" on 152 and "function" on 153. We measured 24 such differences on real builds, and reading the source at the release tags added five more.
  • A server can only tell them apart by what the browser says about itself. The User-Agent, Sec-CH-UA and the full version list change, and the GREASE brand goes from Not?A_Brand/24 to Not_A Brand/8 in a new order. TLS, HTTP/2 and header order are identical.
  • The biggest page-side changes: new Iterator helpers in V8, the <camera> and <microphone> elements, Shared Storage removed, and a Rust XML parser whose error messages read differently (and which Chrome 154 reverted, so it marks 153 alone).
  • For automation: Chrome 153 stops evaluating Proxy getters when DevTools or a CDP client serialises a logged object, which closes one way a page could detect an attached debugger.
  • Unchanged: time zone data, canvas, audio and WebGL render hashes, fonts, speech voices, storage quota and navigator.webdriver.
  • Reproducing this with Google's Chrome for Testing builds needs --disable-field-trial-config. Without it they run Chromium's testing configuration, which changes over a hundred values, the TLS fingerprint among them.

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 152 and Chrome 153. Run them in this browser to see which version its engine matches.

Chrome 152 → Chrome 153 · 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 152Chrome 153This browser
Client Hints
New GREASE brand and brand order in User-Agent Client Hintsnavigator.userAgentData.brands.map((b) => `${b.brand};v=${b.version}`).join(', ')"Chromium;v=152, Not?A_Brand;v=24, Google Chrome;v=152""Google Chrome;v=153, Not_A Brand;v=8, Chromium;v=153"–
JavaScript and DOM
New Iterator helpers: Iterator.zip, Iterator.zipKeyed and Iterator.prototype.jointypeof Iterator.zip"undefined""function"–
New Iterator helpers: Iterator.zip, Iterator.zipKeyed and Iterator.prototype.jointypeof Iterator.zipKeyed"undefined""function"–
New Iterator helpers: Iterator.zip, Iterator.zipKeyed and Iterator.prototype.jointypeof Iterator.prototype.join"undefined""function"–
New <camera> and <microphone> elementstypeof HTMLCameraElement"undefined""function"–
New <camera> and <microphone> elementstypeof HTMLMicrophoneElement"undefined""function"–
Shared Storage removed, with its permissions-policy features'sharedStorage' in windowtruefalse–
Shared Storage removed, with its permissions-policy featurestypeof SharedStorageWorklet"function""undefined"–
Shared Storage removed, with its permissions-policy features'sharedStorageWritable' in HTMLIFrameElement.prototypetruefalse–
Shared Storage removed, with its permissions-policy features'canLoadOpaqueURL' in HTMLFencedFrameElementtruefalse–
Shared Storage removed, with its permissions-policy featuresdocument.featurePolicy.features().includes('run-ad-auction')truefalse–
HTMLElement.autocorrect'autocorrect' in HTMLElement.prototypefalsetrue–
BaseAudioContext.renderQuantumSize'renderQuantumSize' in BaseAudioContext.prototypefalsetrue–
Property order moved on Element, Document and CustomElementRegistryObject.getOwnPropertyNames(Element.prototype).indexOf('customElementRegistry') < Object.getOwnPropertyNames(Element.prototype).indexOf('onfullscreenchange')falsetrue–
Property order moved on Element, Document and CustomElementRegistryObject.getOwnPropertyNames(Document.prototype).indexOf('customElementRegistry') < Object.getOwnPropertyNames(Document.prototype).indexOf('fonts')falsetrue–
Property order moved on Element, Document and CustomElementRegistryObject.getOwnPropertyNames(CustomElementRegistry.prototype).indexOf('initialize') < Object.getOwnPropertyNames(CustomElementRegistry.prototype).indexOf('upgrade')falsetrue–
RTCDataChannel options now reject out-of-range values(() => { const pc = new RTCPeerConnection(); try { pc.createDataChannel('x', { maxRetransmits: -1 }); return 'no throw'; } catch (e) { return e.name; } finally { pc.close(); } })()"no throw""TypeError"–
Parsing and rendering
XML parsing moved to a Rust parser, and its error messages changednew DOMParser().parseFromString('<a><b></a>', 'application/xml').querySelector('parsererror div').textContent.trim()"error on line 1 at column 11: Opening and ending tag mismatch: b line 1 and a""error on line 1 at column 10: Unexpected closing tag: a != b"–
Canvas 2D no longer draws a zero-length stroke(() => { const c = document.createElement('canvas'); c.width = c.height = 20; const x = c.getContext('2d'); x.lineWidth = 10; x.lineCap = 'round'; x.beginPath(); x.moveTo(10, 10); x.lineTo(10, 10); x.stroke(); return x.getImageData(10, 10, 1, 1).data[3]; })()2550–
CSS
New CSS property: scroll-axis-lockCSS.supports('scroll-axis-lock', 'auto')falsetrue–
scroll-marker-group: the tabs and links modes turned offCSS.supports('scroll-marker-group', 'after tabs')truefalse–
<area> is display: none by default(() => { const a = document.body.appendChild(document.createElement('area')); const d = getComputedStyle(a).display; a.remove(); return d; })()"inline""none"–
color-mix() now serialises the implied percentage((s) => { s.color = 'color-mix(in oklab, red 30%, blue)'; return s.color; })(document.createElement('i').style)"color-mix(red 30%, blue)""color-mix(red 30%, blue 70%)"–
Media and GPU
IAMF immersive audio in MP4document.createElement('video').canPlayType('audio/mp4; codecs="iamf"')"""probably"–
IAMF immersive audio in MP4MediaSource.isTypeSupported('audio/mp4; codecs="iamf"')falsetrue–
New WGSL language features: buffer_view and texture_formats_tier1navigator.gpu.wgslLanguageFeatures.has('buffer_view')falsetrue–
New WGSL language features: buffer_view and texture_formats_tier1navigator.gpu.wgslLanguageFeatures.has('texture_formats_tier1')falsetrue–
WebGL shader and program info logs no longer end in a NUL character(() => { const gl = document.createElement('canvas').getContext('webgl'); const s = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(s, 'void main(){ undeclared_x; }'); gl.compileShader(s); const l = gl.getShaderInfoLog(s); return l.charCodeAt(l.length - 1) === 0; })()truefalse–
Automation
DevTools no longer runs Proxy getters when it serialises a logged object(() => { let hit = false; const e = new Error('x'); Object.defineProperty(e, 'stack', { configurable: true, get: new Proxy(function () {}, { apply() { hit = true; return ''; } }) }); console.log(e); return hit; })()truefalsen/a

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 153

TLS ClientHello
Identical on both versions: JA4 t13d1517h2_8daaf6152771_cb7bf5808d99, the same cipher list, the same 19 extensions (including ECH, ALPS and certificate compression), key-share groups X25519MLKEM768, X25519, P-256, P-384 and the same signature algorithms. Two independent TLS echo services agreed. One nuance from the source, not measured: the compiled-in Chrome Root Store went from 32 to 28 trust anchors (152, 153), so a browser that has not yet loaded the root-store component sends a 20-byte-shorter trust_anchors extension. JA3 and JA4 do not hash extension contents, so they are unaffected.
HTTP/2
Identical: Akamai fingerprint 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p, the same HEADERS priority (weight 256, exclusive) and pseudo-header order m,a,s,p.
Request headers
Identical names, order and values for navigation, iframe, fetch, XHR, images, scripts, modules, styles, fonts, media, workers, beacons, WebSocket and CORS preflight requests, except for the values that carry the version: User-Agent, Sec-CH-UA, Sec-CH-UA-Full-Version and Sec-CH-UA-Full-Version-List.

Chrome 153 User-Agent strings and Client Hints

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

The Linux and headless strings were measured. The others follow Chromium's reduced User-Agent format (user_agent_utils.cc), in which 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):

"Google Chrome";v="153", "Not_A Brand";v="8", "Chromium";v="153"

User-Agent and Client Hints

high impactMeasured on both builds

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

Chrome derives a deliberately fake "GREASE" brand and the order of the brand list from the major version, so that sites cannot hard-code the list. On 153 the brand is Not_A Brand version 8 and Google Chrome is listed first. This is what navigator.userAgentData.brands returns and what every request sends in Sec-CH-UA.

navigator.userAgentData.brands.map((b) => `${b.brand};v=${b.version}`).join(', ')
Chrome 152
"Chromium;v=152, Not?A_Brand;v=24, Google Chrome;v=152"
Chrome 153
"Google Chrome;v=153, Not_A Brand;v=8, Chromium;v=153"

Why it matters: The brand list is on every request a server receives. Software that changes the version number but keeps an older brand list or order sends a combination no real Chrome sends. The algorithm is the same file at both tags; only its input, the major, changed.

JavaScript and Web APIs

high impactMeasured on both builds

New Iterator helpers: Iterator.zip, Iterator.zipKeyed and Iterator.prototype.join

V8 15.3 turns on joint iteration and join. They are part of the JavaScript engine, so they exist in every realm: windows, iframes and workers. They are also appended to the end of Iterator's own-property lists.

typeof Iterator.zip
Chrome 152
"undefined"
Chrome 153
"function"
  • typeof Iterator.zipKeyed"undefined"→ "function"
  • typeof Iterator.prototype.join"undefined"→ "function"

Why it matters: The engine answers this, not anything a page or extension can set. A browser that sends a Chrome 153 User-Agent but has no Iterator.zip runs an older engine.

medium impactMeasured on both builds

HTMLElement.autocorrect

The autocorrect attribute is reflected as a property on every HTML element. Google's status page files it under Chrome 152, but the Blink flag was experimental at the 152 tag and stable at 153.

'autocorrect' in HTMLElement.prototype
Chrome 152
false
Chrome 153
true

Why it matters: A new member on HTMLElement.prototype changes its property list, which fingerprinting scripts enumerate.

medium impactMeasured on both builds

BaseAudioContext.renderQuantumSize

Web Audio contexts expose their render quantum size, and OfflineAudioContext accepts a renderSizeHint. The default stays 128 frames, so the usual audio fingerprint hash does not move.

'renderQuantumSize' in BaseAudioContext.prototype
Chrome 152
false
Chrome 153
true

Why it matters: Audio contexts are a common fingerprinting surface, and scripts that probe them see a new member.

medium impactMeasured on both builds

Property order moved on Element, Document and CustomElementRegistry

No property was added or removed here, but some moved. Blink installs unconditional members first and members behind a runtime flag afterwards, so when a flag goes stable its members move up. That is what happened to customElementRegistry on Element, Document and ShadowRoot, and to CustomElementRegistry.prototype.initialize; several WebGPU members moved the same way.

Object.getOwnPropertyNames(Element.prototype).indexOf('customElementRegistry') < Object.getOwnPropertyNames(Element.prototype).indexOf('onfullscreenchange')
Chrome 152
false
Chrome 153
true
  • Object.getOwnPropertyNames(Document.prototype).indexOf('customElementRegistry') < Object.getOwnPropertyNames(Document.prototype).indexOf('fonts')false→ true
  • Object.getOwnPropertyNames(CustomElementRegistry.prototype).indexOf('initialize') < Object.getOwnPropertyNames(CustomElementRegistry.prototype).indexOf('upgrade')false→ true

Why it matters: Property order comes from the build, not from anything a page can configure, so hashing a prototype's key order identifies the version even when every property is present.

low impactRead in the source at both tags

RTCDataChannel options now reject out-of-range values

maxRetransmits and maxPacketLifeTime in createDataChannel() gained [EnforceRange]: a negative value throws a TypeError instead of wrapping to 65535.

(() => { const pc = new RTCPeerConnection(); try { pc.createDataChannel('x', { maxRetransmits: -1 }); return 'no throw'; } catch (e) { return e.name; } finally { pc.close(); } })()
Chrome 152
"no throw"
Chrome 153
"TypeError"

Why it matters: Synchronous and permission-free; WebRTC set-up code is already part of many bot checks.

HTML

high impactMeasured on both builds

New <camera> and <microphone> elements

Chrome 153 ships declarative capability elements for camera and microphone access. They add two globals, HTMLCameraElement and HTMLMicrophoneElement, and document.createElement('camera') now returns a real element instead of HTMLUnknownElement.

typeof HTMLCameraElement
Chrome 152
"undefined"
Chrome 153
"function"
  • typeof HTMLMicrophoneElement"undefined"→ "function"

Why it matters: New globals change the window's property list, which many fingerprinting scripts hash as a whole.

Removed or deprecated

high impactMeasured on both builds

Shared Storage removed, with its permissions-policy features

The Shared Storage API is gone from the page: eight globals (sharedStorage, SharedStorageWorklet and six related interfaces), the sharedStorageWritable attribute on iframes and images, HTMLFencedFrameElement.canLoadOpaqueURL, and five permissions-policy features (shared-storage, shared-storage-select-url, run-ad-auction, join-ad-interest-group, private-aggregation). With the two new elements, the window goes from 1231 to 1225 own properties.

'sharedStorage' in window
Chrome 152
true
Chrome 153
false
  • typeof SharedStorageWorklet"function"→ "undefined"
  • 'sharedStorageWritable' in HTMLIFrameElement.prototypetrue→ false
  • 'canLoadOpaqueURL' in HTMLFencedFrameElementtrue→ false
  • document.featurePolicy.features().includes('run-ad-auction')true→ false

Why it matters: A removal is as clear a version marker as an addition: a Chrome 153 claim with sharedStorage present comes from an older engine. A live 152 may already hide the API through a server-side experiment, so treat its presence as a stronger signal than its absence.

Parsing and rendering behaviour

high impactMeasured on both builds

XML parsing moved to a Rust parser, and its error messages changed

DOMParser and XML responses use a memory-safe Rust XML parser instead of libxml2 for common cases. Well-formed XML parses the same; the text of the error for malformed XML is different. Chrome 154 went back to libxml2 by default, so this message identifies 153 specifically. The switch is behind a feature flag that a server-side experiment can turn off.

new DOMParser().parseFromString('<a><b></a>', 'application/xml').querySelector('parsererror div').textContent.trim()
Chrome 152
"error on line 1 at column 11: Opening and ending tag mismatch: b line 1 and a"
Chrome 153
"error on line 1 at column 10: Unexpected closing tag: a != b"

Why it matters: Error text comes straight from the parser, so one malformed string identifies which parser, and so which Chrome generation, is running.

medium impactRead in the source at both tags

Canvas 2D no longer draws a zero-length stroke

Stroking a path whose only segment has zero length used to draw the line cap (a dot, with round caps). Chrome 153 prunes such paths before stroking, as the HTML specification describes, so nothing is drawn. Any canvas hash that strokes a degenerate path moves.

(() => { const c = document.createElement('canvas'); c.width = c.height = 20; const x = c.getContext('2d'); x.lineWidth = 10; x.lineCap = 'round'; x.beginPath(); x.moveTo(10, 10); x.lineTo(10, 10); x.stroke(); return x.getImageData(10, 10, 1, 1).data[3]; })()
Chrome 152
255
Chrome 153
0

Why it matters: A GPU-independent pixel check that also works in workers through OffscreenCanvas.

CSS

medium impactMeasured on both builds

scroll-marker-group: the tabs and links modes turned off

scroll-marker-group: after tabs and after links were accepted on 152 because of a flag bug. Chrome 153 gates them from 153.0.8010.43 on (earlier 153 builds still accept them), and Chrome 154 turns them on for real. The plain after and before values work throughout.

CSS.supports('scroll-marker-group', 'after tabs')
Chrome 152
true
Chrome 153
false

Why it matters: A value that is true on 152, false on 153 and true again on 154 separates 153 from both of its neighbours in one call. It also separates a 153 built before .43 from one built after. The 152 value comes from a runtime flag, so other Chromium-based builds of 152 (Electron, for one) can already answer false; treat this row as a supporting signal, not a verdict on its own.

medium impactRead in the source at both tags

<area> is display: none by default

The user-agent stylesheet now hides <area> elements, as the HTML specification requires. On 152 they computed to inline.

(() => { const a = document.body.appendChild(document.createElement('area')); const d = getComputedStyle(a).display; a.remove(); return d; })()
Chrome 152
"inline"
Chrome 153
"none"

Why it matters: One deterministic line at the stylesheet level, with no rendering or GPU involved.

low impactMeasured on both builds

color-mix() now serialises the implied percentage

When one percentage is given, the serialised value now spells out the other one, as the CSS Color specification says.

((s) => { s.color = 'color-mix(in oklab, red 30%, blue)'; return s.color; })(document.createElement('i').style)
Chrome 152
"color-mix(red 30%, blue)"
Chrome 153
"color-mix(red 30%, blue 70%)"

Why it matters: Serialisation details are cheap to read and hard to fake without the engine that produces them.

Media and codecs

low impactMeasured on both builds

IAMF immersive audio in MP4

Media elements, Media Source Extensions and WebCodecs can decode the Immersive Audio Model and Formats (IAMF) codec in MP4: the build flag and the feature flag both flipped on.

document.createElement('video').canPlayType('audio/mp4; codecs="iamf"')
Chrome 152
""
Chrome 153
"probably"
  • MediaSource.isTypeSupported('audio/mp4; codecs="iamf"')false→ true

Why it matters: Codec support lists are part of most fingerprints. Measured on Linux; a build without proprietary codecs answers differently.

WebGL and WebGPU

medium impactRead in the source at both tags

WebGL shader and program info logs no longer end in a NUL character

getShaderInfoLog(), getProgramInfoLog() and WEBGL_debug_shaders.getTranslatedShaderSource() returned a string one character too long on 152, ending in U+0000. Chrome 153 trims it.

(() => { const gl = document.createElement('canvas').getContext('webgl'); const s = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(s, 'void main(){ undeclared_x; }'); gl.compileShader(s); const l = gl.getShaderInfoLog(s); return l.charCodeAt(l.length - 1) === 0; })()
Chrome 152
true
Chrome 153
false

Why it matters: Works with any GPU or software renderer, in WebGL 1 and 2, synchronously. It needs a WebGL context, so it throws where WebGL is disabled.

low impactMeasured on both builds

New WGSL language features: buffer_view and texture_formats_tier1

WebGPU's shading language reports two more features, so the list grows from 10 to 12. This reads a compiled-in table; no GPU adapter is needed.

navigator.gpu.wgslLanguageFeatures.has('buffer_view')
Chrome 152
false
Chrome 153
true
  • navigator.gpu.wgslLanguageFeatures.has('texture_formats_tier1')false→ true

Why it matters: Readable without a GPU, in workers too, and it changes with the browser version. Measured on Linux; check other platforms before relying on it.

Automation and DevTools protocol

high impactRead in the source at both tags

DevTools no longer runs Proxy getters when it serialises a logged object

When a debugger is attached with Runtime.enable (DevTools, or a CDP client such as Puppeteer or Playwright), Chrome serialises objects passed to console.log for the debugger. On 152 that serialisation called getters implemented as Proxies, so a page could plant one and learn that a debugger was listening. V8 15.3 no longer evaluates them.

(() => { let hit = false; const e = new Error('x'); Object.defineProperty(e, 'stack', { configurable: true, get: new Proxy(function () {}, { apply() { hit = true; return ''; } }) }); console.log(e); return hit; })()
Chrome 152
true
Chrome 153
false

Why it matters: Only meaningful with a debugger attached: without one both versions return false, which is why the live column skips it. Run it from the DevTools console (which attaches one) on each version. Engines older than 152 very likely behave like 152; only 152 was checked.

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.

Reproduce these results

  1. 1. Get both versions

    Use the stable Google Chrome packages of the two exact versions. On Linux, Google keeps every version in its package pool, so you can download them side by side and extract them without installing:

    bash
    for v in 152.0.7977.82 153.0.8010.52; do
      curl -sSO https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${v}-1_amd64.deb
      dpkg -x google-chrome-stable_${v}-1_amd64.deb chrome-${v}
    done
  2. 2. Or use Chrome for Testing, with one extra flag

    Google's Chrome for Testing builds (the ones Puppeteer downloads) have the same code, but by default they run Chromium's testing field-trial configuration. That changes over a hundred values, including an extra TLS extension, so JA4 reads t13d1518h2_… instead of t13d1517h2_…. Start them with --disable-field-trial-config and they match the branded build apart from the brand name.

    bash
    curl -sSO https://storage.googleapis.com/chrome-for-testing-public/153.0.8010.52/linux64/chrome-linux64.zip
    unzip -q chrome-linux64.zip
    ./chrome-linux64/chrome --disable-field-trial-config --user-data-dir="$(mktemp -d)"
  3. 3. Start each one clean

    Use a new profile directory for each run and no automation flags, so you measure the browser and not your tooling. Headless works too (--headless=new); every value on this page is the same headless and headful.

    bash
    ./chrome-153.0.8010.52/opt/google/chrome/chrome --user-data-dir="$(mktemp -d)" --no-first-run https://example.com
  4. 4. Run the checks

    Press "Run the checks in this browser" at the top of this page, or open DevTools on any https:// page and paste the script below. Every line prints the check and the value your browser returns; compare it with the two columns on this page.

  5. 5. Check the network side

    Open a TLS echo service such as tls.peet.ws or tls.browserleaks.com in each version and compare the JA4 and HTTP/2 (Akamai) fingerprints with the values above.

  6. 6. Find a change in the source yourself

    The page-visible surface of every Chromium release is recorded in Blink's test expectations, and every feature's status in one JSON file. Diff them between the two release tags in a Chromium checkout, or read the commit log on gitiles:

    bash
    git diff 152.0.7977.85 153.0.8010.55 -- third_party/blink/web_tests/virtual/stable/webexposed/
    git diff 152.0.7977.85 153.0.8010.55 -- third_party/blink/renderer/platform/runtime_enabled_features.json5
    git log --oneline 152.0.7977.85..153.0.8010.55 -- third_party/blink/renderer/core/css/

The whole check list as one script

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

chrome-153-probe.js
// Chrome 152 vs 153: paste into the DevTools console on any https:// page and press Enter.
// Each line prints "<change>  <check> = <value>". Expected values: clearcotelabs.com/chrome-releases/153
(async () => {
  const checks = [
    ["grease-brand", () => navigator.userAgentData.brands.map((b) => `${b.brand};v=${b.version}`).join(', ')],
    ["iterator-helpers", () => typeof Iterator.zip],
    ["iterator-helpers", () => typeof Iterator.zipKeyed],
    ["iterator-helpers", () => typeof Iterator.prototype.join],
    ["camera-microphone-elements", () => typeof HTMLCameraElement],
    ["camera-microphone-elements", () => typeof HTMLMicrophoneElement],
    ["shared-storage-removed", () => 'sharedStorage' in window],
    ["shared-storage-removed", () => typeof SharedStorageWorklet],
    ["shared-storage-removed", () => 'sharedStorageWritable' in HTMLIFrameElement.prototype],
    ["shared-storage-removed", () => 'canLoadOpaqueURL' in HTMLFencedFrameElement],
    ["shared-storage-removed", () => document.featurePolicy.features().includes('run-ad-auction')],
    ["xml-parser-rust", () => new DOMParser().parseFromString('<a><b></a>', 'application/xml').querySelector('parsererror div').textContent.trim()],
    ["devtools-proxy-getters", () => (() => { let hit = false; const e = new Error('x'); Object.defineProperty(e, 'stack', { configurable: true, get: new Proxy(function () {}, { apply() { hit = true; return ''; } }) }); console.log(e); return hit; })()],
    ["html-autocorrect", () => 'autocorrect' in HTMLElement.prototype],
    ["render-quantum-size", () => 'renderQuantumSize' in BaseAudioContext.prototype],
    ["property-order", () => Object.getOwnPropertyNames(Element.prototype).indexOf('customElementRegistry') < Object.getOwnPropertyNames(Element.prototype).indexOf('onfullscreenchange')],
    ["property-order", () => Object.getOwnPropertyNames(Document.prototype).indexOf('customElementRegistry') < Object.getOwnPropertyNames(Document.prototype).indexOf('fonts')],
    ["property-order", () => Object.getOwnPropertyNames(CustomElementRegistry.prototype).indexOf('initialize') < Object.getOwnPropertyNames(CustomElementRegistry.prototype).indexOf('upgrade')],
    ["datachannel-enforce-range", () => (() => { const pc = new RTCPeerConnection(); try { pc.createDataChannel('x', { maxRetransmits: -1 }); return 'no throw'; } catch (e) { return e.name; } finally { pc.close(); } })()],
    ["canvas-zero-length-stroke", () => (() => { const c = document.createElement('canvas'); c.width = c.height = 20; const x = c.getContext('2d'); x.lineWidth = 10; x.lineCap = 'round'; x.beginPath(); x.moveTo(10, 10); x.lineTo(10, 10); x.stroke(); return x.getImageData(10, 10, 1, 1).data[3]; })()],
    ["scroll-axis-lock", () => CSS.supports('scroll-axis-lock', 'auto')],
    ["scroll-marker-group-modes", () => CSS.supports('scroll-marker-group', 'after tabs')],
    ["area-display-none", () => (() => { const a = document.body.appendChild(document.createElement('area')); const d = getComputedStyle(a).display; a.remove(); return d; })()],
    ["color-mix-serialization", () => ((s) => { s.color = 'color-mix(in oklab, red 30%, blue)'; return s.color; })(document.createElement('i').style)],
    ["iamf-audio", () => document.createElement('video').canPlayType('audio/mp4; codecs="iamf"')],
    ["iamf-audio", () => MediaSource.isTypeSupported('audio/mp4; codecs="iamf"')],
    ["wgsl-language-features", () => navigator.gpu.wgslLanguageFeatures.has('buffer_view')],
    ["wgsl-language-features", () => navigator.gpu.wgslLanguageFeatures.has('texture_formats_tier1')],
    ["webgl-shader-log-nul", () => (() => { const gl = document.createElement('canvas').getContext('webgl'); const s = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(s, 'void main(){ undeclared_x; }'); gl.compileShader(s); const l = gl.getShaderInfoLog(s); return l.charCodeAt(l.length - 1) === 0; })()],
  ];
  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 153 released?

Chrome 153 reached the stable channel on September 8, 2026. It was the first release on Google's two-week release cycle, so Chrome 154 followed on September 22.

What is the Chrome 153 user agent string?

On Windows it is Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.0.0 Safari/537.36. Chrome reduces the User-Agent, so the minor version is always 0.0.0; the full version is only sent in Client Hints. The table above lists every platform.

Did Chrome 153 change the TLS (JA3/JA4) fingerprint?

No. The ClientHello is identical to Chrome 152: JA4 t13d1517h2_8daaf6152771_cb7bf5808d99, the same extensions, groups and signature algorithms.

Did Chrome 153 change the HTTP/2 fingerprint?

No. SETTINGS, window update, priority and pseudo-header order are unchanged: 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p.

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

"Google Chrome";v="153", "Not_A Brand";v="8", "Chromium";v="153". The fake GREASE brand and the order of the list both change with the major version.

How can a website tell Chrome 152 from Chrome 153?

With any of the checks on this page. The simplest is typeof Iterator.zip, which is "function" only on 153 and later. "Run the checks in this browser" at the top runs all of them.

Is Chrome for Testing the same as Google Chrome?

Same code, different defaults. Chrome for Testing applies Chromium's testing field-trial configuration, which on 153 changed over a hundred observable values, sent an extra TLS extension (a different JA4) and moved the Accept-Language header. With --disable-field-trial-config it matches Google Chrome apart from the brand name, an information bar and the absence of Google's network speech voices.

Which V8 version does Chrome 153 use?

V8 15.3 (15.3.76.13 in 153.0.8010.52), up from 15.2 (15.2.124.21) in Chrome 152.

Clearcote puts this into practice

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

Free for one browser with GitHub. No card.