HTTP/2 fingerprinting, and how the Akamai string is put together
Your client now sends a ClientHello that hashes like Chrome, the User-Agent says Chrome, and the site still blocks you before any page logic runs. The handshake is covered in JA3 and JA4 TLS fingerprinting. The next layer is HTTP/2, and its first frames are as much a product of the library that wrote them as the handshake was.
Where the reference format comes from
The format most tools still print was proposed by Akamai researchers in the paper Passive Fingerprinting of HTTP/2 Clients, presented at Black Hat Europe. HTTP/2 makes the client announce preferences before the first request, the protocol leaves most of them to the implementation, and every implementation picks differently. Nothing there depends on the user, so it identifies the software, not the person. The paper looks at four things.
SETTINGS: which parameters, in what order, with what values
Every HTTP/2 connection starts with a SETTINGS frame from each side. The client's frame lists parameters by numeric ID: 1 header table size, 2 enable push, 3 max concurrent streams, 4 initial window size, 5 max frame size, 6 max header list size. The paper found clients differ in three ways at once: which parameters they bother to send, the order they send them in, and the values.
A browser that never sends 3 and a library that always sends 3:100 are already distinguishable, before you look at a single value. Copying the numbers from a capture without copying their order does not help.
WINDOW_UPDATE: one number
Right after SETTINGS, almost every client sends a connection-level WINDOW_UPDATE that raises its receive window. The increment is whatever the implementation's authors decided their buffer should be, so it varies between clients and stays constant within one.
PRIORITY frames
Some clients open the connection by sending PRIORITY frames for streams they will never use, to build a dependency tree that later requests hang off. Each frame has a stream ID, an exclusivity bit, the stream it depends on, and a weight. The paper's examples show one browser family sending five of these on every connection while others send none. The current HTTP/2 specification deprecates that priority scheme, so on a live capture you will often see this field empty. An empty field is still a value: a client that claims to be a browser which sends them, and does not, has contradicted itself.
Pseudo-header order
HTTP/2 replaces the request line with four pseudo-headers: :method, :authority, :scheme, :path. The spec does not fix their order, so each implementation writes them in whatever order its header map happens to keep. In the paper's captures Chrome sent :method, :authority, :scheme, :path, Firefox :method, :path, :authority, :scheme, and curl and Go each had their own order again. Regular header order after the pseudo-headers is a separate signal that many detectors read too, but it is not part of the Akamai string.
The fingerprint string
The paper's suggested format concatenates the four parts with pipes:
S[;]|WU|P[,]#|PS[,]
S: each SETTINGS parameter asid:value, joined by;in the order received.WU: the WINDOW_UPDATE increment,00if the frame was not sent.P: each PRIORITY frame asstream:exclusive:depends_on:weight, joined by,, or0if there were none.PS: pseudo-header order as letters,m,a,s,p.
The paper's worked example, a Firefox capture, reads:
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|m,p,a,s
Those numbers are from browsers of that time. Current browsers send different values, so do not hard-code anything from the paper. Read your own client's string from a live echo service instead.
Why it is never checked on its own
The paper itself says an HTTP/2 fingerprint does not carry enough entropy to track a person. What it does well is confirm or contradict a claim. A request arrives saying User-Agent: ... Chrome/.... The TLS fingerprint says which TLS library built the ClientHello, and that ClientHello's ALPN extension already announced h2. The HTTP/2 fingerprint says which HTTP/2 implementation framed the request. If all three name the same browser, that request has passed a cheap consistency check. If the UA says Chrome, JA4 says Chrome, and the HTTP/2 string says Go's net/http, the request is lying somewhere, and no header edit reaches the layer that gave it away.
The paper also points out the reverse use. A client whose TCP stack looks like Windows while its TLS and HTTP/2 look like a Mac browser is probably behind a proxy that forwards bytes without terminating TLS.
What this means if you build clients
This layer belongs to the network stack, not to your request code. A library that impersonates a browser has to reproduce all four parts together, in step with the browser version it claims. Whether that is enough depends on what else the site checks.
AntiBrow does not rewrite the HTTP/2 layer. A profile's frames come from the browser's own network stack, so this layer is not something you configure in it. If your only problem is an HTTP/2 or TLS mismatch on a JSON endpoint, a browser is the expensive way to fix it, and an impersonating HTTP library is the better tool.
Check your own client
Point the client you actually deploy at https://tls.peet.ws/api/all. The response includes an akamai_fingerprint field built in the format above, next to the TLS fingerprints. Load the same URL in normal Chrome on the same machine and diff the two strings field by field. For where this check sits among the other signals a bot score uses, see how anti-bot systems work.
Try it on the free tier.
Unlimited local profiles, no credit card. Check it against the detectors yourself.