HTTP/2 fingerprint (SETTINGS preface) changed within one session

net.h2_unstable_within_session  ·  convicts

networklayer
coherencecategory
1evaders caught

What it catches

Within-session HTTP/2 coherence — the FOURTH member of the within-session network-invariant rotation family (net.ja4_unstable_within_session = TLS engine, net.ip_rotation_within_session = network origin, net.ua_rotation_within_session = UA string; this = the HTTP/2 stack), and DISTINCT from JA4 rotation. The edge's network.h2 is the Akamai HTTP/2 fingerprint — SETTINGS frame + WINDOW_UPDATE increment + PRIORITY frames + the request PSEUDO-header order (edge fingerprint/h2.go String()) — all connection-PREFACE choices a client's HTTP/2 stack makes ONCE per connection and holds fixed per browser build (the pseudo-header order is request-type-invariant; the regular header order is deliberately NOT in the string). So a real client emits ONE h2 fingerprint for the session's lifetime, the HTTP/2 analog of its invariant JA4_b. Two distinct network.h2 under one ks_sid is a single client rotating its HTTP/2 stack mid-session. CRITICALLY NON-REDUNDANT with ja4_unstable: a tool can pin ONE TLS ClientHello (one JA4_b — the uTLS HelloChrome profile) yet vary its h2 SETTINGS across connections (different MAX_FRAME_SIZE / MAX_HEADER_LIST_SIZE → a different SETTINGS frame), so JA4 stays constant and ja4_unstable never fires while the h2 fingerprint rotates — the gap this rule closes. The detector derives network.h2_unstable in ingest over the pre-collapse per-request h2 history (the store otherwise keeps only the latest h2 per kind — the same merge-collapse blind spot as JA4/IP/UA); sticky once tripped. FP-safe by construction: the h2 String is a connection-preface invariant (a real browser sends the identical SETTINGS/window/priority/pseudo-order on every connection in a session), and browserforge calibration carries no network layer, so promotion cannot raise its legit-browser flag rate (same class as its JA4/IP/UA siblings). GROUNDED LIVE 2026-06-20: the go-tls KS_H2ROTATE evader opened 3 connections under ONE ks_sid, each with the SAME uTLS HelloChrome ClientHello (one JA4_b — ja4_unstable stays QUIET, proving the gap is distinct from TLS rotation) but a DIFFERENT http2.Transport SETTINGS profile (default / big-frame / small-frame) → the detector accumulated 3 distinct network.h2 and tripped this rule (label bot); the single-connection go-tls (one h2 fingerprint) and every single-h2 evader/real-browser capture do NOT fire it — zero FP. Residual caveat (documented, same external-proxy class as net.ja4_unstable's): an HTTP/2-terminating forward proxy that re-frames a session's connections through different h2 stacks could present >1 h2 fingerprint, but the lab edge is the first hop (sees the client directly), so that residual is an external-proxy edge case, not an in-fleet FP.

Signals it reads

network.h2_unstable

How it fires

present

Evaders it caught 1

Bypassed by 10

Frontier evaders that reach the detector uncaught (scored only suspicious, defeating every convicting tell) — this check is not one that stops them. The red-team frontier this detection still has to convict.

← all detections