HTTP/2 fingerprinting identifies a client from how it opens an HTTP/2 connection — the parameters it sends in its initial SETTINGS frame, and the order of its four pseudo-headers (:method, :authority, :scheme, :path) — hashed into the JA4H format, a companion to JA4's TLS fingerprint that reads the application layer instead of the handshake.
The first thing any HTTP/2 client sends after the connection opens is a SETTINGS frame, and browsers disagree on which of the six possible parameters they send, what values they use, and in what order — Chrome 119+ sends only four of six, with a distinctive INITIAL_WINDOW_SIZE value, while Python's hyper-h2 or Go's net/http libraries default to a different set entirely. Every subsequent request then opens with four pseudo-headers whose order the spec leaves flexible, and each browser has settled on its own fixed sequence (Chrome: method/authority/scheme/path; Firefox: method/path/authority/scheme; Safari: method/scheme/path/authority) that a generic HTTP library rarely reproduces by accident.
JA4H packs both signals into one pipe-delimited hash — SETTINGS parameters in frame order, the WINDOW_UPDATE increment, any PRIORITY frame, and the pseudo-header sequence — giving anti-bot vendors a single comparable string to match against a database of known browser and library signatures, the same pattern JA3/JA4 uses one layer down at the TLS handshake.
A scraper that adopts curl_cffi or a similar tool to match Chrome's JA3/JA4 TLS handshake still opens its HTTP/2 connection through whatever library sits above that TLS layer — and if that library is Python's hyper-h2 rather than Chrome's own network stack, its SETTINGS frame and header order won't match, producing a TLS fingerprint that reads as Chrome sitting on top of an HTTP/2 fingerprint that reads as Python. Cloudflare, Akamai, and DataDome all cross-check TLS and HTTP/2 fingerprints against each other precisely because that combination — coherent at one layer, wrong at the next — is a much higher-confidence bot signal than either mismatch alone.
Closing the gap means the same tool that impersonates the TLS handshake has to drive the HTTP/2 layer too, not just layer a matching User-Agent and TLS fingerprint on top of a mismatched connection library.
How Crawlora handles this
Crawlora's requests keep TLS and HTTP/2 fingerprints consistent with each other and with the browser identity being presented, so a target's cross-layer check doesn't catch the kind of TLS-fixed-but-HTTP/2-unfixed mismatch that a partially-impersonated stack leaves behind.
FAQ
They're companion techniques at different layers — JA3/JA4 reads the TLS handshake before any HTTP data is exchanged, while JA4H reads the HTTP/2 SETTINGS frame and header order that follow it. Anti-bot systems increasingly check both together, since a real browser's traffic is coherent on both.
Yes — that's exactly the gap it's designed to close. A tool that impersonates only the TLS layer can still leave its underlying HTTP/2 library's default SETTINGS and header order unchanged, which a cross-layer check flags as inconsistent.
It avoids JA4H specifically, but most modern browsers negotiate HTTP/2 by default via TLS's ALPN extension — a client that only ever speaks HTTP/1.1 to an HTTP/2-capable site is itself an anomaly many anti-bot systems flag, so it trades one signal for another rather than removing detection.
Beyond HTTP/2 Fingerprinting (JA4H), Crawlora's own docs cover the rest of the stack — browse the APIs, test a request in Playground, and move from scraping infrastructure work to production data workflows.