Skip to content

fix(http2): wire custom HTTP/2 fingerprints to the transport - #61

Merged
smeinecke merged 1 commit into
mainfrom
fix-http2-fingerprint
Aug 31, 2026
Merged

fix(http2): wire custom HTTP/2 fingerprints to the transport#61
smeinecke merged 1 commit into
mainfrom
fix-http2-fingerprint

Conversation

@smeinecke

Copy link
Copy Markdown
Owner

Summary

  • Fix http2_fingerprint so the supplied HTTP/2 identity is actually emitted on the wire.
  • Rewrite golang/http2.go to parse the canonical SETTINGS|WINDOW_UPDATE|PRIORITY_FRAMES|PSEUDO_HEADERS format and populate fhttp's http2.HTTP2Settings.
  • Update golang/roundtripper.go to apply those settings to the transport and to set the pseudo-header order from the fingerprint.

Background

The Go layer parsed http2_fingerprint but only assigned it to http2.Transport.Settings. That field is overwritten by Transport.ApplyPreset, which derives the real HTTP/2 frame profile from the Navigator (parsed from the User-Agent). As a result, every request was emitted with the default Firefox- or Chrome-style frame sequence regardless of the supplied http2_fingerprint.

What changed

  • HTTP2Fingerprint now carries the full fingerprint:
    • Settings from the first part
    • ConnectionFlow from the second part
    • PriorityFrames from the third part
    • HeaderPriority (reserved for future use)
    • PriorityOrder pseudo-header map from the fourth part
  • Apply sets http2.Transport.HTTP2Settings instead of just Settings.
  • roundTripper parses the fingerprint once and uses it when creating the http2.Transport and when preparing each request (http.PHeaderOrderKey).
  • HTTP2Fingerprint.Navigator() infers the minimal fhttp browser default needed for the implicit HEADERS stream ID when no explicit priority frames are present.

Test plan

  • go test ./... in golang/ passes.
  • Built a fresh libcycletls and verified with the matrix of Firefox/Chrome/Edge/custom http2_fingerprint strings that the wire Akamai fingerprint now matches the supplied value.
  • Verified named profiles (firefox_124_win, chrome_149_*) now produce coherent HTTP/2 frames.
  • Confirmed the pseudo-header order (m,p,a,s vs m,a,s,p) follows the fingerprint, not just the User-Agent.

Notes

This does not change how ja3 / ja4r TLS fingerprints are handled; it only makes http2_fingerprint an independent, working lever. Profiles that already ship both ja4r and http2_fingerprint now produce a matching TLS + HTTP/2 identity.

The http2_fingerprint option was parsed but never actually emitted on the
wire. The old HTTP2Fingerprint.Apply only wrote Transport.Settings, which
fhttp's Transport.ApplyPreset immediately overwrote with hardcoded Firefox/
Chrome defaults derived from the User-Agent. The fhttp HTTP2Settings struct
(ConnectionFlow, PriorityFrames, HeaderPriority, and Settings) was left empty,
so connection flow, initial priority frames, and the HEADERS stream ID stayed
driven by the UA regardless of the supplied fingerprint.

Rewrite HTTP2Fingerprint parsing to match the canonical
SETTINGS|WINDOW_UPDATE|PRIORITY_FRAMES|PSEUDO_HEADERS format and populate
http2.HTTP2Settings. Apply that to the transport and use the fingerprint to
pick the remaining fhttp defaults (e.g. the implicit HEADERS stream ID when no
priority frames are present). Also map the pseudo-header order letters onto
http.PHeaderOrderKey so the request header ordering matches the fingerprint too.

This makes http2_fingerprint a first-class option and lets profiles that ship
both ja4r and http2_fingerprint produce a coherent TLS + HTTP/2 identity.
@smeinecke
smeinecke force-pushed the fix-http2-fingerprint branch from 20719c9 to 1425119 Compare August 31, 2026 13:30
@smeinecke
smeinecke merged commit 863809f into main Aug 31, 2026
38 checks passed
@smeinecke
smeinecke deleted the fix-http2-fingerprint branch August 31, 2026 13:58
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