feat: add chrome151 browser fingerprint - #510
Merged
Merged
Conversation
Picks up apify/rustls#22, which adds the ML-DSA variants to `FingerprintSignatureAlgorithm`. Required by the chrome151 fingerprint, which lists those three codepoints first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Chrome stable is 151; the newest profile was chrome142. The only wire-level TLS difference between them is that Chrome now leads its signature_algorithms list with the three ML-DSA codepoints (0x0904/0x0905/0x0906), which accounts for the entire JA4_c divergence reported in apify#508. Captured against tls.peet.ws from consumer Chrome stable 151.0.7922.72 on Windows 11, cross-checked against Chrome for Testing 151.0.7922.71 on the same host. The two agree on everything except sec-ch-ua, which Chrome for Testing cannot supply because it is unbranded. Verified: JA4, JA4_r and all seventeen request headers are byte-identical to the consumer browser, and a real request completes with ML-DSA advertised. Requires the matching ML-DSA change in apify/rustls; until that lands and the rev in Cargo.toml is bumped, this will not build. Headers carry the captured Windows platform rather than the macOS strings used by chrome142, so the UA and sec-ch-ua-platform stay internally consistent. initial_connection_window_size is 15728640 (15 MiB) rather than the 15663105 used by sibling profiles: the field is the window size, not the increment, so this is what makes the emitted WINDOW_UPDATE equal the 15663105 seen on the wire. Closes apify#508 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
barjin
approved these changes
Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a
chrome151fingerprint. Closes #508.Relative to
chrome142the only wire-level TLS difference is that Chrome now leads itssignature_algorithmslist with the three ML-DSA codepoints0x0904/0x0905/0x0906, which is the entire reasonchrome142produces a different JA4 from current Chrome. Ciphers, extensions, key exchange groups, ALPN, ALPS, ECH and the HTTP/2 settings are unchanged.Depends on apify/rustls#22 (merged), which added the ML-DSA variants to
FingerprintSignatureAlgorithm; the first commit here bumps therevpin to23b2c17.Result
t13d1516h2_8daaf6152771_806a8c22fdeachrome151(this PR)t13d1516h2_8daaf6152771_806a8c22fdeachrome142t13d1516h2_8daaf6152771_d8a2da3f94cdja4_ris byte-identical to the browser:All seventeen request headers also match byte-for-byte in wire order, and a real request completes with status 200 while ML-DSA is advertised.
How it was captured
Against
https://tls.peet.ws/api/allfrom consumer Chrome stable 151.0.7922.72 on Windows 11, driven headed via Playwright with a throwaway profile.Chrome for Testing was used as a cross-check and validated first: CfT 150.0.7871.124 against consumer Chrome stable 150.0.7871.187 on the same host produced identical JA4, JA4_r, ciphers, extensions, groups, signature algorithms and HTTP/2 settings. CfT 151.0.7922.71 then agreed with the consumer 151 capture on everything except
sec-ch-ua, which CfT cannot supply because it is unbranded — so that header comes from the consumer build. It is stable across launches (verified over four fresh profiles).Two things worth knowing about the capture:
chrome142rather than any single capture.pre_shared_key, givingt13d1517h2_..._a87ad97598a9. This profile targets the cold-connection shape, which is what the issue measured and whatchrome142was compared against.Headers carry the captured Windows platform
chrome142advertises macOS. This profile keeps the platform it was actually captured on, sosec-ch-ua-platformand the UA string stay internally consistent. Happy to normalise to macOS if you would rather the Chrome profiles stay uniform, but that would mean constructing a UA string rather than using a captured one.One deviation from sibling profiles, deliberately
initial_connection_window_sizeis15_728_640where the other Chrome and Firefox profiles use the wire-observed increment directly.The field is passed to
reqwest'shttp2_initial_connection_window_size, which h2 treats as the target window and from which it derives theWINDOW_UPDATEincrement astarget - 65535. So15_663_105emits15_597_570, i.e. 65535 short of real Chrome, whereas15_728_640(15 MiB) emits exactly the15_663_105the capture shows.This matches
safari.rs, which already documents the same arithmetic (// 65_535 (h2 default) + 10_420_225 WINDOW_UPDATE = 10_485_760.), and matches the value #385 originally asked for. The older Chrome/Firefox profiles look like they took the number from that issue's comparison table rather than its recommendation. Left them alone here since changing them would alter existing profiles' output.Not addressed here
Two pre-existing HTTP/2 gaps show up when diffing against the browser. Both affect every profile equally and need changes to
Http2Fingerprint, so they are out of scope — happy to open issues:SETTINGS_HEADER_TABLE_SIZE = 65536; there is no field for it. (HTTP/2 SETTINGS values are not fingerprinted — all profiles share hyper defaults #385 notes reqwest/h2 does not expose it.)SETTINGS_MAX_FRAME_SIZE = 16384, which Chrome omits as the protocol default.Separately, on a resumed connection rustls drops
session_ticketwhen it addspre_shared_key, where Chrome sends both — so a second request to the same host produces a JA4 no real Chrome emits. That one is in the rustls emulation layer, not here.