Skip to content

Improve Chrome and HTTP/2 parity handling#418

Open
minanagehsalalma wants to merge 1 commit intoDanny-Dasilva:mainfrom
minanagehsalalma:upstream-pr/chrome-h2-parity
Open

Improve Chrome and HTTP/2 parity handling#418
minanagehsalalma wants to merge 1 commit intoDanny-Dasilva:mainfrom
minanagehsalalma:upstream-pr/chrome-h2-parity

Conversation

@minanagehsalalma
Copy link
Copy Markdown

Summary

This PR improves browser parity for Chrome-like requests by tightening the TLS, HTTP/2, and header-order paths.

Changes included:

  • add a deterministic modern Chrome-style ClientHelloSpec for Chrome user agents
  • use Firefox uTLS auto preset for Firefox user agents instead of forcing the default Chrome JA3 path
  • apply HTTP/2 fingerprint settings through HTTP2Settings, including connection flow and initial header priority
  • preserve explicit HTTP/2 regular header order without re-marshalling the header map
  • preserve lowercase user-agent placement for fhttp header-order handling
  • update Go/uTLS dependencies required by the newer Chrome/ECH/ALPS primitives

Context

This came out of parity testing where matching only JA3 and surface headers was not enough. The missing pieces were lower-level Chrome ClientHello behavior, HTTP/2 connection behavior, and strict header order preservation.

Notes

This PR intentionally does not include fork branding, screenshots, or the case-study docs from CycleTLS-Parity; it only contains the upstreamable transport changes.

Testing

  • git diff --cached --check passed locally before commit
  • Full Go tests were not run in this Windows shell because go is not available on PATH here

@minanagehsalalma
Copy link
Copy Markdown
Author

minanagehsalalma commented Apr 23, 2026

What this patch fixes in practice

The public fingerprint endpoints below return 200 both before and after, so the useful signal is not HTTP status. The useful signal is whether CycleTLS actually emits the browser fingerprint the caller requested.

Before this patch, several browser-parity requests succeeded as HTTP requests but failed as spoofed browser requests: the server observed the wrong TLS generation, the wrong HTTP/2 SETTINGS, or Chrome and Firefox collapsing into the same TLS fingerprint.

Failure modes fixed

Scenario Before origin/main After this PR Benefit
Request a Chrome-style H2 profile Failed exact H2 match. Reported 1:65536;3:1000;4:6291456;5:16384;6:262144|15663105|0|m,a,s,p Matches requested profile. Reports 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p The caller can ask for Chrome-like H2 behavior without leaking old/default MAX_CONCURRENT_STREAMS and MAX_FRAME_SIZE settings.
Request a Firefox-style H2 profile Failed exact H2 match. Missing 2:0 / ENABLE_PUSH = 0 Includes 2:0. Reports 1:65536;2:0;4:131072;5:16384... The Firefox profile is materially closer to what the caller requested.
Use different Chrome vs Firefox user agents Failed UA separation. Both paths reported the same TLS JA4: t12d1515h2_8daaf6152771_4d8a99c1bc01 Distinct browser TLS paths. Chrome reports t13d1516h2_8daaf6152771_d8a2da3f94cd; Firefox reports t13d1715h2_5b57614c22b0_5c2c66f702b0 A Firefox UA no longer silently rides the same Chrome-ish/default TLS shape.
Modern Chrome-like default path Older TLS shape. tls.peet.ws saw t12... Modern TLS 1.3 shape. tls.peet.ws sees t13... Better default browser parity for targets that score TLS generation, extensions, and ALPN together.

Repro data

Tested against:

  • https://tls.peet.ws/api/all
  • https://tools.scrapfly.io/api/fp/anything

Compared builds:

  • Before: upstream origin/main at 28f3d8b
  • After: this PR branch at 3cf86bb

Focused test run on this PR branch:

npm test -- --runInBand tests/http2-fingerprint.test.js tests/frameHeader.test.ts
PASS tests/frameHeader.test.ts
PASS tests/http2-fingerprint.test.js
Test Suites: 2 passed, 2 total
Tests: 6 passed, 6 total

Exact observed values

Endpoint Case Before After
tls.peet.ws Chrome TLS JA4 t12d1515h2_8daaf6152771_4d8a99c1bc01 t13d1516h2_8daaf6152771_d8a2da3f94cd
tls.peet.ws Chrome H2 fingerprint 1:65536;3:1000;4:6291456;5:16384;6:262144|15663105|0|m,a,s,p 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p
tls.peet.ws Firefox TLS JA4 t12d1515h2_8daaf6152771_4d8a99c1bc01 t13d1715h2_5b57614c22b0_5c2c66f702b0
tls.peet.ws Firefox H2 fingerprint 1:65536;4:131072;5:16384|12517377|3:0:0:201,5:0:0:101,7:0:0:1,9:0:7:1,11:0:3:1,13:0:0:241|m,p,a,s 1:65536;2:0;4:131072;5:16384|12517377|3:0:0:201,5:0:0:101,7:0:0:1,9:0:7:1,11:0:3:1,13:0:0:241|m,p,a,s
tools.scrapfly.io Chrome TLS JA3 digest a046fe17d33822fb5db1a14b3e8d940a dd3974b729ed1f6caf80a902f3d05128
tools.scrapfly.io Chrome H2 fingerprint 1:65536;3:1000;4:6291456;5:16384;6:262144|15663105|0|m,a,s,p 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p
tools.scrapfly.io Firefox TLS JA3 digest a046fe17d33822fb5db1a14b3e8d940a fc21b639f5d6037589b3fead714e5039
tools.scrapfly.io Firefox H2 fingerprint 1:65536;4:131072;5:16384|12517377|3:1:0:201,5:1:0:101,7:1:0:1,9:1:7:1,11:1:3:1,13:1:0:241|m,p,a,s 1:65536;2:0;4:131072;5:16384|12517377|3:1:0:201,5:1:0:101,7:1:0:1,9:1:7:1,11:1:3:1,13:1:0:241|m,p,a,s

In short: this patch makes CycleTLS better at doing what its API already promises: if a caller requests Chrome/Firefox-like TLS + HTTP/2 behavior, the observed wire fingerprint now matches that request much more closely instead of silently falling back to generic/default behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant