Skip to main content

Akamai Shorthand

The Akamai HTTP/2 fingerprint is a compact string that captures how a client opens an H2 connection. It's the H2 equivalent of a JA3. Anti-bot vendors hash it and check against a known-browser allowlist, same playbook as JA3.

Format

Four pipe-separated fields:

SETTINGS|WINDOW_UPDATE|PRIORITY|PSEUDO_HEADER_ORDER

Real Chrome 148:

1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p

Real Firefox 148:

1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s

Real iOS Safari 18:

2:0;4:2097152;3:100;5:16384;9:1|10485760|0|m,s,p,a

SETTINGS

Semicolon-separated id:value pairs from the SETTINGS frame. Standard H2 IDs:

IDNameNotes
1HEADER_TABLE_SIZEChrome 65536, Firefox 65536, Safari omits
2ENABLE_PUSHAll browsers send 0
3MAX_CONCURRENT_STREAMSSafari 100, Chrome / Firefox omit
4INITIAL_WINDOW_SIZEChrome 6291456, Firefox 131072, Safari 2097152
5MAX_FRAME_SIZEFirefox 16384, Safari 16384, Chrome omits
6MAX_HEADER_LIST_SIZEChrome 262144, others omit
9NO_RFC7540_PRIORITIESSafari 1, others omit

Pair order in the string matches wire-frame order. Chrome ships 1, 2, 4, 6. Firefox: 1, 2, 4, 5. Safari: 2, 4, 3, 5, 9. iOS Chrome's slightly different: 2, 3, 4, 9. Match the order or your akamai hash won't.

WINDOW_UPDATE

The connection-level WINDOW_UPDATE increment sent right after SETTINGS. Chrome 148: 15663105. Firefox 148: 12517377. Safari 18: 10485760.

PRIORITY

The H2 PRIORITY frame value. 0 means no PRIORITY frame goes out, which is what Chrome / Firefox / Safari all do as of 2026. They signal priority via the priority HTTP header instead. Older Chrome versions used to send a stream weight here.

PSEUDO_HEADER_ORDER

Comma-separated single-char identifiers for the order of pseudo-headers in the first HEADERS frame:

  • m = :method
  • a = :authority
  • s = :scheme
  • p = :path

Chrome: m,a,s,p. Firefox: m,p,a,s. Safari: m,s,p,a. iOS Chrome: m,s,a,p. Every browser's different and the akamai hash captures it.

When to override

Use the akamai shorthand override when you want to keep the preset's TLS handshake but tweak the H2 fingerprint. Common cases:

  • A target rejects the default Chrome H2 settings but takes a slightly larger initial window.
  • You captured an akamai string from a real browser and want to mirror it exactly.
  • You're spoofing a Chrome version we haven't shipped yet that bumped a single SETTINGS value.

Need anything beyond H2 SETTINGS, like overriding the priority table, the HPACK header order, or per-request priorities? Hop to the JSON Preset Builder.

API

WithCustomFingerprint accepts a JA3 and an akamai string. Set one, the other, or both.

package main

import (
"context"
"fmt"
"io"

"github.com/sardanioss/httpcloak"
)

func main() {
s := httpcloak.NewSession("chrome-148-windows",
httpcloak.WithCustomFingerprint(httpcloak.CustomFingerprint{
// Keep Chrome's TLS, override only H2.
Akamai: "1:65536;2:0;4:8388608;6:262144|15663105|0|m,a,s,p",
}),
)
defer s.Close()

resp, _ := s.Get(context.Background(), "https://tls.peet.ws/api/all")
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println(string(body))
}

How shorthand interacts with the preset

When you set Akamai, the parser fills in only the slots that appear in your string. Slots you skip keep the preset's value.

  • SETTINGS pairs you list overwrite the preset's same-ID values.
  • SETTINGS IDs you don't list keep the preset's value.
  • A non-empty WINDOW_UPDATE overrides, empty keeps the preset.
  • A non-zero PRIORITY weight enables the H2 PRIORITY frame, zero disables it.
  • A non-empty pseudo-header order overrides, empty keeps the preset's.

So you can write a minimal patch and the rest of the H2 state stays correct:

1::|||

That's a valid akamai string that says "set HEADER_TABLE_SIZE to its default, leave everything else alone". In practice you'll want three or four fields for it to be useful.

Verifying

Send a request through the override, read tls.peet.ws's http2.akamai_fingerprint field. It matches what you sent:

input akamai: 1:65536;2:0;4:8388608;6:262144|15663105|0|m,a,s,p
output akamai (peet): 1:65536;2:0;4:8388608;6:262144|15663105|0|m,a,s,p
output akamai_hash: <stable MD5 over the string>

If the reflected akamai string doesn't match exactly, the parser dropped a field. Most common cause: typo in the SETTINGS pair list. 1:65536;2:0 is fine, 1=65536,2=0 is not. The parser expects colon-separated pairs joined by semicolons.

warning

akamai_fingerprint_hash is an MD5 of the akamai string with sorted SETTINGS keys. Two strings that differ only in SETTINGS order produce the same hash. So 1:65536;4:6291456 and 4:6291456;1:65536 hash identically even though the strings differ. Wire-level SETTINGS frame order still matters for the H2 fingerprinters that look past the basic akamai hash, those care about the unsorted string. Always send fields in the order the real browser does.