Skip to main content

Presets

A preset is the full fingerprint bundle for one browser version on one platform. It packs:

  • TLS ClientHello (cipher list, extension list, supported groups, signature algorithms, ALPN, cert compression).
  • HTTP/2 SETTINGS values, WINDOW_UPDATE, pseudo-header order.
  • Default HTTP headers in the exact order Chrome / Firefox / Safari ships them.
  • RFC 7540 stream priorities and the RFC 9218 priority table per Sec-Fetch-Dest.
  • HTTP/3 / QUIC transport parameters (only on presets that support h3).
  • TCP/IP fingerprint hints (TTL, MSS, window size, for OS-level matching).

Pick one by name, send a request, the wire bytes match the real browser.

Picking the right preset

  • Default to chrome-latest. Works against the widest range of targets, and auto-tracks the newest Chrome we've shipped.
  • Reach for android-chrome-latest when you need a mobile UA. Mobile traffic gets scored differently on most anti-bot stacks. The TLS handshake is identical to desktop Chrome, but the User-Agent and sec-ch-ua-mobile: ?1 route the request onto the mobile path.
  • Use ios-safari-18 (or safari-18-ios) for an iPhone fingerprint. Different cipher list, different pseudo-header order, no RFC 7540 priorities, smaller QUIC stream window. Targets that profile iOS users will spot a Chrome preset pretending to be an iPhone in seconds.
  • Pick firefox-148 when the target only accepts Firefox. Different cipher list, different SETTINGS layout (smaller initial window, smaller max frame size), different pseudo-header order (m,p,a,s vs Chrome's m,a,s,p).

Available preset families

Chrome

Versions 133, 141, 143, 144, 145, 146, 147, 148, 149. The 143-148 line ships per-OS variants; 133 and 141 are desktop-only single presets without per-OS suffixes. Chrome 149 is desktop-only for now (no mobile capture yet). Layout for the per-OS line (using 148 as the example):

FamilyVariants
Desktopchrome-148, chrome-148-windows, chrome-148-linux, chrome-148-macos, chrome-149, chrome-149-windows, chrome-149-linux, chrome-149-macos
Androidchrome-148-android (alias: android-chrome-148)
iOSchrome-148-ios (alias: ios-chrome-148)

Bare chrome-148 resolves to the host OS at runtime via runtime.GOOS. On a Linux box, chrome-148 gives you chrome-148-linux. For the same platform UA regardless of where the code runs, use the explicit variant.

Chrome -latest aliases

Aliases that auto-track the newest shipped Chrome:

chrome-latest → chrome-149
chrome-latest-windows → chrome-149-windows
chrome-latest-linux → chrome-149-linux
chrome-latest-macos → chrome-149-macos
chrome-latest-android → chrome-148-android
chrome-latest-ios → chrome-148-ios

Chrome 149 has shipped, so the desktop -latest aliases now resolve to 149, while the mobile aliases stay on 148 until a 149 mobile capture lands. Code on chrome-latest keeps rolling, and code that pinned chrome-148-windows stays on the same fingerprint.

Firefox

firefox-133, firefox-148, firefox-latest. No per-OS variants, since Firefox doesn't bake enough OS info into its fingerprint to matter. No h3 yet either; Firefox has its own h3 quirks we haven't built out.

Safari

PresetNotes
safari-18 (safari-latest)Desktop macOS Safari 18, supports h3
safari-17-ios (ios-safari-17)iPhone Safari 17, h2 only
safari-18-ios (ios-safari-18, safari-latest-ios)iPhone Safari 18, supports h3

Safari sets NoRFC7540Priorities=true, so it never emits the H2 PRIORITY frame, and RFC 9218 priority headers carry the signal instead. That's the single biggest tell that splits a Safari fingerprint from a Chrome one at the H2 layer, even though both ALPN as h2.

Backwards-compat aliases

The older <os>-<browser>-<version> naming still works for code written against earlier docs:

ios-chrome-148 → chrome-148-ios
ios-safari-18 → safari-18-ios
android-chrome-148 → chrome-148-android

Both forms resolve to the same preset.

Inheritance: how a new Chrome version ships in 30 seconds

Each Chrome minor bump is usually pure UA plus sec-ch-ua delta. TLS fingerprint, H2 SETTINGS, header order, priority table, all the same as the version before. Chrome 148 isn't a from-scratch Go file; it's a JSON delta over Chrome 147:

{
"version": 1,
"preset": {
"name": "chrome-148-windows",
"based_on": "chrome-147-windows",
"headers": {
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36",
"values": {
"sec-ch-ua": "\"Chromium\";v=\"148\", \"Google Chrome\";v=\"148\", \"Not/A)Brand\";v=\"99\""
},
"order": [
{"key": "sec-ch-ua", "value": "\"Chromium\";v=\"148\", \"Google Chrome\";v=\"148\", \"Not/A)Brand\";v=\"99\""},
{"key": "sec-ch-ua-mobile", "value": "?0"},
...
]
}
}
}

That's the whole patch. TLS bytes come from chrome-147-windows, which itself inherits TLS bytes from chrome-146-windows since nothing changed in 147. H2 SETTINGS, priority table, everything else, all inherited.

The same path is open to you. Pick a preset, dump it, change three fields, register the result. See JSON Preset Builder.

Verification

Hit tls.peet.ws/api/all with each preset and you'll see the matching JA4 / Akamai hash:

package main

import (
"context"
"fmt"
"io"

"github.com/sardanioss/httpcloak"
)

func main() {
for _, name := range []string{"chrome-latest", "android-chrome-148", "firefox-148", "safari-18-ios"} {
s := httpcloak.NewSession(name)
resp, _ := s.Get(context.Background(), "https://tls.peet.ws/api/all")
body, _ := io.ReadAll(resp.Body)
resp.Body.Close()
s.Close()
fmt.Println(name, string(body))
}
}

Captured fingerprints (run on 2026-05, against tls.peet.ws/api/all):

chrome-latest ja4=t13d1516h2_8daaf6152771_d8a2da3f94cd peetprint_hash=1d4ffe9b0e34acac0bd883fa7f79d7b5 akamai_fingerprint_hash=52d84b11737d980aef856699f885ca86
chrome-148-windows ja4=t13d1516h2_8daaf6152771_d8a2da3f94cd peetprint_hash=1d4ffe9b0e34acac0bd883fa7f79d7b5 akamai_fingerprint_hash=52d84b11737d980aef856699f885ca86
chrome-148-linux ja4=t13d1516h2_8daaf6152771_d8a2da3f94cd peetprint_hash=1d4ffe9b0e34acac0bd883fa7f79d7b5 akamai_fingerprint_hash=52d84b11737d980aef856699f885ca86
chrome-148-macos ja4=t13d1516h2_8daaf6152771_d8a2da3f94cd peetprint_hash=1d4ffe9b0e34acac0bd883fa7f79d7b5 akamai_fingerprint_hash=52d84b11737d980aef856699f885ca86
android-chrome-148 ja4=t13d1516h2_8daaf6152771_d8a2da3f94cd peetprint_hash=1d4ffe9b0e34acac0bd883fa7f79d7b5 akamai_fingerprint_hash=52d84b11737d980aef856699f885ca86
firefox-148 ja4=t13d1717h2_5b57614c22b0_3cbfd9057e0d peetprint_hash=89d89662b21018947a9a46658c4f5ede akamai_fingerprint_hash=6ea73faa8fc5aac76bded7bd238f6433
safari-18 ja4=t13d2013h2_a09f3c656075_7f0f34a4126d peetprint_hash=62b834de729e78a9f0ebd1dd099314a7 akamai_fingerprint_hash=90d8353e47699c4c38ecd773e9b5a089
safari-18-ios ja4=t13d2013h2_a09f3c656075_7f0f34a4126d peetprint_hash=62b834de729e78a9f0ebd1dd099314a7 akamai_fingerprint_hash=90d8353e47699c4c38ecd773e9b5a089
chrome-148-ios ja4=t13d2013h2_a09f3c656075_7f0f34a4126d peetprint_hash=62b834de729e78a9f0ebd1dd099314a7 akamai_fingerprint_hash=c52879e43202aeb92740be6e8c86ea96

Things to spot:

  • Every Chrome desktop variant lands on the same JA4 / peetprint / akamai. The TLS handshake is identical across Windows / Linux / macOS Chrome. Only the User-Agent and sec-ch-ua-platform header tell you which OS you're on.
  • Android Chrome shares the same fingerprint as desktop Chrome. Same TLS, same H2. The wire-level difference is the UA string (Mobile Safari/537.36) and sec-ch-ua-mobile: ?1.
  • Chrome on iOS shows up as Safari at the wire level, since iOS WebKit forces every browser onto the system networking stack. chrome-148-ios shares its TLS handshake and JA4 hash with safari-18-ios. They split only on H2 SETTINGS values (chrome-148-ios advertises 2,3,4,9 vs Safari's 2,4,3,5,9) and the User-Agent.
  • Firefox and Safari each get their own JA4 / peetprint / akamai. Different cipher list, different SETTINGS, different pseudo-header order.
tip

The bare ja3_hash field won't be stable for Chrome presets across runs. Chrome shuffles its TLS extension order on every connection, so the raw JA3 string changes and the MD5 changes with it. JA4 sorts the extension list before hashing, which is why it's stable. Always verify against ja4 and peetprint_hash, never ja3_hash.

Full preset catalog

69 preset names total (counting -latest aliases and the old <os>-<browser> naming). For the exhaustive table with version numbers, supported protocols, and platform tags, see the Presets reference.