# Why Chrome's JA3 hash changes on every connection

> Chrome's JA3 changes because Chrome shuffles its TLS extension order on every connection, and JA3 hashes that order. JA4 sorts first and holds still.

Source: https://mosaicbrowser.com/blog/why-chromes-ja3-changes

## Why Chrome's JA3 hash changes on every connection

Chrome's JA3 changes because Chrome shuffles its TLS extension order on every connection, and JA3 hashes that order. JA4 sorts first and holds still.

![Two JA3 hashes, one JA4, beside a screenshot: routing Mosaic launches through Proxyman, Charles or mitmproxy to read their traffic](https://mosaicbrowser.com/covers/why-chromes-ja3-changes.webp)

Chrome's JA3 changes because Chrome shuffles the order of the extensions in its TLS ClientHello on every new connection, and JA3 hashes those extensions in the order they arrive. Change the order and the hash changes, even though nothing else about the browser did. Chrome does this on purpose. A JA3 that stays fixed for a client claiming to be Chrome is therefore the odd one out: it repeats an ordering real Chrome almost never sends twice.

## What JA3 actually hashes

JA3 takes five fields from the ClientHello, the first message a browser sends when it opens a TLS connection: the TLS version, the cipher suites, the extensions, the elliptic curves and the point formats. It writes each list out in the order it was sent, joins the five with commas, and takes an MD5 of the string.

That order is the weak point. When JA3 was designed, a given TLS library always sent its extensions in the same sequence, so the sequence was a stable trait of the client. Hashing it in order made sense. Nobody had a reason to shuffle.

## Why does Chrome's JA3 change? It shuffles on purpose

Chrome randomises its extension order to stop servers and middleboxes depending on it. When every client sends one exact shape, equipment on the network starts assuming that shape, and the protocol can no longer change without breaking somebody. The word for that is ossification. A browser that varies the parts that are allowed to vary keeps the rest of the internet from hard-coding them.

Chrome also sends GREASE values, deliberately meaningless entries in several lists, for the same reason. Between the two, a fixed JA3 was never something Chrome produced to begin with.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

[How it works: Machines that exist](https://mosaicbrowser.com/features/real-fingerprints)

## What we measured

We did not take this on faith, because our own engine depends on it. Tesseract Chrome is Chromium, so it should emit exactly Chrome's handshake. We ran one profile against Google's own Chrome for Testing 153.0.8010.36, on the same machine in the same minute, through our detector runner's `tls-ja3` row with a stock control:

```
ours     JA4 t13d1518h2_8daaf6152771_4980c97edce0
stock    JA4 t13d1518h2_8daaf6152771_4980c97edce0

```

Byte-identical: TLS 1.3, 15 cipher suites, 18 extensions, ALPN `h2`, and the same hashes of the sorted cipher list and extension set. The JA3 values were `995f48479d09` for ours and `624b4feda192` for stock. That gap is not a finding. Each connection draws a new ordering, so two connections from one binary disagree on JA3 as readily as two binaries do.

JA4 survives because it sorts the ciphers and extensions before hashing them, and leaves GREASE out. The shuffle changes the order and nothing else, so a sorted list hashes the same every time. The readable prefix, `t13d1518h2`, says TCP, TLS 1.3, a domain name in SNI, 15 ciphers, 18 extensions and HTTP/2.

## The version number moves it, not the shuffle

There was one number that did change, and it came from the wrong control. Google Chrome 152 on the same machine read `t13d1517h2`: seventeen extensions where 153 sends eighteen. That is a change in Chrome's own TLS stack between major versions.

So the honest comparison is always against a Chrome of the same major. Against a different one, a real difference in Chrome itself looks like a fault in yours. Our stock-comparison check refuses to compare across majors for this reason.

## Why our engine has no TLS fingerprint switch

Some anti-detect browsers sell control over the TLS fingerprint as a feature. We left it out deliberately. Every real Chrome of a given version sends the same ClientHello shape, so varying it per profile does not separate two profiles from each other; it makes one of them the only browser on the internet with that handshake.

A handshake that disagrees with the User-Agent is also the earliest contradiction a site can read, before a single line of JavaScript runs. A profile that claims Chrome and opens its connection like something else has failed before the page loads. Ours inherits Chrome's permutation and GREASE untouched, and the same holds for the HTTP/2 settings Chrome sends, because both come from the Chromium code we build.

## How to read your own

Look at the JA4, not the JA3, and compare it with a real Chrome of the same major on the same machine. Our [TLS fingerprint page](https://mosaicbrowser.com/tools/tls-fingerprint) explains each field of both hashes, and the [fingerprint checker](https://mosaicbrowser.com/tools/fingerprint-checker) shows what a page reads once the connection is open. If you are weighing products that promise a "custom JA3", [what an anti-detect browser is](https://mosaicbrowser.com/guides/what-is-an-anti-detect-browser) covers which values a browser can change and which it should leave alone, and [our launch post](https://mosaicbrowser.com/blog/introducing-mosaic) explains why we build the engine from stock Chromium source.

## Questions

### Why does Chrome's JA3 change?

Chrome shuffles the order of the extensions in its TLS ClientHello on every new connection, on purpose, and JA3 hashes the extensions in the order they were sent. The same browser therefore produces a different JA3 from one connection to the next.

### Is a changing JA3 a sign that something is wrong?

No. For a modern Chrome it is the normal behaviour. A Chrome-claiming client whose JA3 never changes is the unusual one, because it is repeating a single ordering that real Chrome almost never sends twice.

### What should I compare instead of JA3?

JA4, or any fingerprint that sorts the cipher and extension lists before hashing. Our engine and Google's own Chrome for Testing of the same version produced the identical JA4, t13d1518h2_8daaf6152771_4980c97edce0, while their JA3 values differed.

### Does Mosaic let me set a custom TLS fingerprint?

No, and on purpose. Tesseract Chrome is Chromium, so it sends Chrome's own ClientHello, which is the most common handshake there is. A per-profile TLS persona would make one profile the only browser with that handshake.

## Related

[What is an anti-detect browser?](https://mosaicbrowser.com/guides/what-is-an-anti-detect-browser): An anti-detect browser gives each profile its own fingerprint, proxy and cookies, so sites see separate visitors. What it changes, how, and its limits.

[Introducing Mosaic, an anti-detect browser that lives on your machine](https://mosaicbrowser.com/blog/introducing-mosaic): Mosaic runs its own Chromium, draws each profile from a real machine and matches it to the proxy exit. Profiles stay on disk. One plan, no per-profile fees.













## Mosaic, on your own computer

The browser this blog is about: its own Chromium, fingerprints from real machines and a proxy exit per profile. Free holds 3 profiles. Pro is $49 a month for any number.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
