You can put Chrome/154.0.0.0, but the server sees the TLS handshake first, then HTTP/2 parameters, and only then the request headers. If those layers do not agree, the string “Chrome 154” does not turn an HTTP client into Chrome.
We compared the main public library families for impersonating browser network fingerprints with BlankTrail Proxy: ready-made profiles, attempts to impersonate Chrome 154, Firefox 156, Safari on macOS and iPhone, and Chrome on Android, as well as repeat connections, HTTP/2 and HTTP/3. We also asked a practical question: can you select the required version using the supported settings without rewriting the library, and how hard is it to integrate into your stack?
Research snapshot: October 4, 2026. Real requests from Windows, Linux and macOS, server-side measurements, TCP and QUIC ClientHello records, and captures from real browsers. For a quick public check, use BlankTrail /client, while the detailed analysis can be repeated using an independent public service. Detector summaries and raw-field comparisons are evaluated separately.

What the server actually compares
A TLS fingerprint comes from how a client builds its ClientHello: the cipher set and order, extensions and their contents, supported groups, signature algorithms, ALPN, GREASE, certificate compression, ALPS and key shares. For HTTP/2, the important details include SETTINGS and their order, WINDOW_UPDATE, pseudo-header order and prioritization parameters. At the HTTP layer, User-Agent, Client Hints and context-specific headers also matter.
JA4 is a useful summary, not a complete description of a connection. It is normal for several consecutive Chrome versions to share a JA4. In our reference captures, Chrome 152, 153 and 154 have the same fresh JA4 and basic HTTP/2 fingerprint. Those matches alone neither establish the exact version nor prove that extension contents are identical. For example, JA4 does not preserve the entire payload of trust_anchors , and excludes GREASE when calculating the relevant parts of the hash. The algorithm is documented in the JA4 repository.
How we compared the clients
The targets were Chrome 154, Edge 154, Firefox 156, Safari 26 and mobile profiles. We compared TLS, HTTP/2, raw HTTP/3 fields, headers and repeat connections against real browser captures. We tested both ready-made presets and supported manual configuration. Matching JA4, or receiving no warnings from /client, does not replace raw-field analysis.
Methodology, reference versions and measurement conditions
Libraries ran in separate test directories, while BlankTrail was controlled through its API. For the main configurations, we made four consecutive requests in one client session. The test endpoint closed the connection so that the next request could create a new TLS connection and use the ticket it had received. Another request on an already open HTTP/2 session was not counted as a TLS resumption test.
For each configuration, we saved the response from /client and the server-side record: TLS fields, PSK acceptance, HTTP/2 SETTINGS, connection window and pseudo-header order. We checked the brief detection result against the raw data.
Fresh Chrome 154 was measured using real Chrome 154.0.8037.93 on Windows with a separate headless profile. For the full ClientHello and resumption, we used saved captures from Chrome 154.0.8037.57 in the official Stable package, taken in headed mode under Xvfb. The package source and SHA256 were verified.
The TCP receiver saved ClientHello before completing the handshake. Its deliberate closure was not counted as a client error. We compared stable parameters and structure: random keys, random and session ID are expected to differ. For GREASE, we checked presence, position and shuffling, and for trust_anchors, the identifier set.
With HTTPX and primp, some repeat requests to the endpoint that closes connections failed while handling the closed HTTP/2 connection. We did not count those errors as fingerprint mismatches: those rows show only the network records actually collected, and incomplete runs are marked separately.
We tested 20 clients: browser profiles, raw TLS fields, HTTP/2 and HTTP/3, headers and repeat-connection behavior. The references were captures of real Chrome 154, Edge 154, Firefox 156, Safari 26.6.2 and mobile browsers. A matching JA4 alone did not count as a pass: we compared stable fields and protocol behavior, with the limits of confirmation stated alongside the results.
For the browser columns, we tested the available ready-made profiles and, where an API existed, manual parameters. We ran request series against a shared endpoint with the UAs of Chrome 154, Safari 26.6.2, Firefox 156, Edge 154 and Chrome Android 154, saving raw ClientHello and server-side TLS/H2 fields. In the Go series, idle connections were closed between requests while retaining the client and its cache. Windows curl was also tested with four URLs in one process. Built-in Node fetch was tested separately without replacing its dispatcher. Changing the UA alone does not count as configuring a transport profile.
Why a profile’s age matters
In the StatCounter sample, Chrome 154 accounted for more than half of versioned desktop Chrome traffic nine days after release, while Chrome 153 took thirteen days. An old preset quickly stops representing most traffic. The entire profile needs updating, not just the version number in User-Agent, although an old version alone does not prove automation. However, we observed that browser version matters greatly to most antifraud systems, and falling more than three major versions behind makes antibot checks harder to pass.

Data source and limitations
Calculated from daily data in StatCounter Desktop / Worldwide, September 8 to October 1, within recognized, versioned Chrome traffic. Android is excluded. These are page-view shares, not installs or unique users. Methodology: FAQ StatCounter, chart licensed under CC BY-SA3.0.
Regular Chrome 154 Stable was released on September 22. Starting with Chrome 153, the major-release cycle was shortened to two weeks. Sources: Chrome 154 release, Chrome release cycle.
Which solutions we tested
We compared public TLS impersonation families, their wrappers and ordinary HTTP clients as controls. This ranks the capabilities tested, not popularity: comparable user-count data is unavailable. Wrappers around the same core are not treated as independent TLS engines.
| Solution / tested version | Stack and integration | How the profile is changed |
|---|---|---|
| curl-impersonate and active fork, via curl_cffi 0.16.3 | CLI/libcurl and Python. Convenient for scripts, but the impersonate build is essential, ordinary curl is not equivalent. | Ready-made targets, libcurl options, and JA3/Akamai and extra_fp through the wrapper. We measured this family through curl_cffi, without separately running the original fork’s binary. |
| curl_cffi 0.16.3 | Python, a requests-like interface, sync/async, requires the bundled native libcurl. | impersonate, ja3, akamai, extra_fp. No ready-made chrome154 in the tested version. |
| uTLS 1.8.2 | Go, low level. Controls ClientHello, but HTTP/2 and headers must be coordinated separately. | Hello profiles, HelloCustom, ApplyPreset, ClientHelloSpec. HelloChrome_Auto in this version points to Chrome 133, not the current Stable. |
| Go tls-client 1.16.0 | Go and a native core for wrappers, with TLS and HTTP/2 combined. Requires replacing the HTTP client in your code. | Ready-made ClientProfile, PSK variants and custom profiles. The newest ready-made Chrome is 152, there is no profile named 154. |
| Python tls-client 1.0.1 | Python, a simple Session API with a native library. The wrapper version does not establish how current the Go core is. | client_identifier and custom TLS/HTTP2 parameters. Chrome presets in the tested distribution stop at 120. |
| noble-tls 0.1.9 / native 1.16.0 | Python async. Updates native tls-client separately from the Python package, so the core version must be pinned for reproducibility. | Preset enum, public client_identifier and custom settings. The loaded core accepts 152/152_PSK even though the wrapper enum lags behind. |
| CycleTLS 2.0.5 | Go/Node.js. Node launches a helper process whose lifecycle must be managed. | JA3, JA4R and HTTP2Fingerprint. Manual configuration depends on the core’s own extension support. |
| rnet 2.4.2 | A native Python client, sync/async. An old distribution cannot automatically be treated as current wreq. | Impersonate enum, tested Chrome preset: 137, no Chrome 154 name. |
| wreq, Python 0.12.3 | Rust and a Python binding, sync/async, convenient browser/platform selection, but the API differs from requests. | Emulation(Profile, Platform), TLS/H2 settings. The latest Chrome in the tested Python package is 153. |
| wreq-js 3.2.0 | Node.js/Bun, a fetch-like API and native Rust binding. The wrapper has its own update schedule. | browser, os, public TLS/H2 overrides. Ready-made Chrome profiles stop at 149. |
| primp 2.0.1 | Python sync/async, a simple client on a native engine. Selecting an OS and browser takes little code. | impersonate / impersonate_os, ready-made Chrome 153. The tested constructor has no public full ClientHelloSpec. |
| got-scraping 4.2.1 | Node.js ESM, similar to got. The project is declared EOL and recommends impit. | Header generation and TLS transport settings. Selecting a version in the header generator does not create a corresponding full ClientHello. |
| impit 0.14.5 | Node.js/Python, a fetch-like API and native Rust core. Convenient for new HTTP-client code. | browser presets, the tested Node package includes Chrome 151 and rejects Chrome 154. |
| req/v3 3.61.0 | Go, high level with a fluent API. TLS and HTTP/2 can be configured through public methods. | ImpersonateChrome, SetTLSFingerprint, SetTLSFingerprintSpec, H2 settings. Built-in ImpersonateChrome uses Chrome 120. |
| httpcloak 1.7.2 | Go and wrappers, tested through Python. Ready-made presets with convenient JSON export/import. | preset, JA3/Akamai, describe_preset and load_preset_from_json. The latest Chrome preset is 152, a custom 154 profile was tested separately. |
| BlankTrail Proxy 1.3.15 | A standalone HTTP/SOCKS proxy application for different languages, including legacy and third-party software. Configured at the client level. Impersonating the HTTPS transport requires installing a certificate in the system or ignoring certificate verification in the client. | Ready-made TLS/HTTP2/HTTP3 browser profiles, browser/OS selection or matching by User-Agent. The product maintains the profile database, no library needs to be embedded in your code. |
An easy API does not remove the maintenance work. If an application already uses requests, switching to another Python client is usually a localized change. A library in a different language may require a binding, a separate service or a transport rewrite. For closed-source third-party software, embedding a new library is often impossible. Updating the package, native core, selected preset and your own headers are four separate tasks.
Comparing libraries with real Chrome 154
The tested distributions had no ready-made Chrome 154 preset as of October 3, so we used the newest available or supported automatic profile with User-Agent154. Exceptions were BlankTrail’s ready-made Chrome_154_win, the got-scraping header generator and manual CycleTLS configuration. Direct selection of 154 and public configuration capabilities were tested separately. Below are the JA4/H2 matches, followed by raw differences those summaries hide.

TLS and HTTP/2 results for each profile
| Client / selected profile | Fresh JA4 | HTTP/2 | What still needs checking or fixing |
|---|---|---|---|
| curl_cffi / chrome150 | Different | Matched | No trust_anchors, signature algorithms lack the reference Chrome 154 GREASE. |
| Go tls-client / Chrome_152 and Chrome_152_PSK | Matched | Matched | Raw captures confirmed matches in the stable TLS fields checked, including GREASE, ALPS and 28 trust anchors. Headers were aligned with 154 through the public API. The standard profile produced four full handshakes, 152_PSK produced one full handshake and three accepted resumptions. |
| Python tls-client / chrome_120 | Different | Matched | Old group set and ALPS code, no trust_anchors. The name chrome_154 does not produce a valid new profile. |
| noble-tls / chrome_152_PSK, core 1.16.0 | Matched | Matched | Raw captures confirmed matches in the stable TLS fields checked, including GREASE, ALPS and 28 trust anchors. Headers were aligned with 154 through the public API, with three accepted resumptions. There is no ready-made 154 name, and the native core version needs to be pinned. |
| rnet / Chrome137 | Different | Matched | Old signature_algorithms, no trust_anchors. No resumption was observed in four requests. |
| wreq Python / Chrome153 | Matched | Matched | trust_anchors contains an empty list, sec-ch-ua remained at 153 despite User-Agent154. |
| wreq-js / chrome_149 | Different | Matched | No ready-made 154, public overrides have no parameter for arbitrary trust_anchors contents. |
| primp / chrome_153 | Matched | Matched | Empty trust_anchors, no GREASE in signature_algorithms, different ALPS payload, sec-ch-ua is 153. The response run was incomplete. |
| got-scraping / Chrome 154 generator | Different | Different | Browser headers do not reproduce Chrome SETTINGS and TLS extensions. |
| impit / chrome151 | Different | Different | SETTINGS lacks Chrome’s HeaderTableSize and adds MaxFrameSize, repeat TLS also differs. |
| req / ImpersonateChrome | Different | Different | TLS preset 120, additional SETTINGS_MAX_CONCURRENT_STREAMS=1000. |
| uTLS HelloChrome_Auto + Go HTTP/2 | Different | Different | TLS profile 133 and separately configured Go H2. uTLS does not promise automatic HTTP/2 configuration. |
| httpcloak / chrome-latest-windows | Matched | Matched | Original Client Hints refer to 152. The stable TLS fields checked, including a nonempty trust-anchor set, matched reference 154. |
| CycleTLS / copied Chrome 154 JA3 and H2 | Connection not established | The core returned an error: extension 51764 is unsupported. | |
| BlankTrail / Chrome_154_win | Matched | Matched | Client Hints154, the stable TLS fields checked and the transition to resumed JA4 matched the reference. |
Control clients requests 2.34.2, HTTPX 0.28.1, Node fetch, Go net/http and system curl 8.13.0 with Schannel also differ from Chrome 154. This is expected: their purpose is to make HTTP requests correctly, not reproduce a browser. Even with HTTP/2 enabled, HTTPX sends its own SETTINGS. Supporting HTTP/2 does not in itself mean matching Chrome.
Now let’s look beyond the JA4 hash
Identical JA4 does not guarantee identical ClientHello: the hash does not compare every extension payload. Below is a combined raw TCP/TLS analysis for the Chrome 154 target.
We compared ciphers, groups, signature_algorithms, TLS versions, key_share groups and lengths, extension sets and stable payloads, including ALPN, ALPS, GREASE and trust_anchors. Random bytes in keys, ECH, tickets and binders were not required to match byte for byte. Permitted reordering was evaluated against the browser reference.
| Client | Tested profile | TLS fields | Repeat TLS | Matches / differences found |
|---|---|---|---|---|
| BlankTrail | Chrome_154_win | ✓ | ✓ | TLS: The TLS fields checked, GREASE, ALPS and trust_anchors matched. Repeat: PSK accepted, JA4 transition matched the reference. |
| Go tls-client | 152 and 152_PSK | ✓ | △ | TLS: 8 ClientHello records: the fields checked, ALPS and 28 anchors matched. Repeat: 152: 0/4, 152_PSK: 3/4. The server selected PSK, extension 41 was last. |
| noble-tls | native1.16.0, 152 and 152_PSK | ✓ | △ | TLS: 8 ClientHello records: the fields checked and anchors matched. Repeat: 152: 0/4, 152_PSK: 3/4. PSK acceptance confirmed by ServerHello. |
| httpcloak | latest | ✓ | ✓ | TLS: The fields checked and the nonempty anchor set matched. Repeat: PSK works in the tested configuration. |
| wreq Python | Chrome153 | △ | ✓ | TLS: JA4 matched, but trust_anchors is an empty list, 0000. Signature algorithms and ALPS matched. Repeat: PSK works, but the empty TLS payload remains a difference. |
| primp | chrome_153 | ✕ | ✕ | TLS: Empty trust_anchors, no GREASE in signature_algorithms, different ALPS payload. Repeat: The required PSK transition was not obtained. |
| curl_cffi | chrome150 | ✕ | ✕ | TLS: No trust_anchors or reference 154 GREASE in signature algorithms. Repeat: The required TCP transition was not reproduced. |
| Python tls-client | chrome_120 | ✕ | △ | TLS: Old groups and ALPS code, no trust_anchors. The name chrome_154 does not create a new profile. Repeat: Partial, differences in the old TLS profile remain. |
| rnet | Chrome137 | ✕ | △ | TLS: Old signature_algorithms, no trust_anchors. Repeat: Original series: 0/4, overall assessment is partial. |
| wreq-js | chrome_149 | ✕ | △ | TLS: Old profile, overrides do not set the anchor payload. Repeat: Partial, the exact target sequence is unconfirmed. |
| req/v3 | ImpersonateChrome | ✕ | ✕ | TLS: Built-in TLS profile 120 does not match154. Repeat: The required Chromium PSK transition was not obtained. |
| uTLS + Go HTTP/2 | Native transport | ✕ | △ | TLS: HelloChrome_Auto uses profile 133. Anchors can be customized, but no ready-made154 was obtained. Repeat: Partial, requires separate configuration. |
| impit | chrome151 | ✕ | ✕ | TLS: TLS profile 151 and the repeat handshake differ. Repeat: The repeat fingerprint differs. |
| CycleTLS | JA3 + H2 Chrome154 | ✕ | ✕ | TLS: Chrome 154 JA3: unsupported extension 51764 error. Repeat: No successful sequence in this mode, Safari tested separately. |
| got-scraping | headers154 | ✕ | ✕ | TLS: Native TLS with browser headers. Repeat: The browser transition was not reproduced. |
| Controls: HTTPX, Go net/http, Windows curl, requests, Node fetch | Native transport | ✕ | △ | TLS: Native ClientHello, no ready-made Chrome 154 emulation. Repeat: Go with cache and Node: PSK accepted, nonbrowser TLS. The others did not produce the required transition. |
✓ - matched in the tested configuration, ✕ - target result not obtained, △ - partial or profile-dependent. TLS fields are assessed against Chrome 154. PSK acceptance alone does not mean the entire repeat fingerprint matches. Go tls-client and noble-tls results depend on choosing the standard or PSK profile.
The key difference hidden by JA4: wreq/primp trust_anchors contains 0000, while the reference and matching clients have 28 IDs, 186 bytes including the list length. primp’s ALPS is 000403c9bb32, rather than the reference 0003026832 with h2. The anchor count applies to this capture: Chrome Root Store updates independently of the browser version.
Headers were checked separately: wreq/primp combined User-Agent154 with Client Hints153. These can be aligned through the public API, but that will not fix the empty TLS payload.
Can you move to a newer browser version without rewriting the library?
Having no preset named Chrome 154 is not the same as being unable to reproduce its behavior. We tested direct profile names, available public parameters and several manual configurations.
- curl_cffi:
impersonate="chrome154"is rejected. Passing the reference JA3 and Akamai values alongside chrome150 completed the request, but the wire still showed the old profile’s JA4 without trust_anchors. Accepting a configuration does not mean applying it fully. - Python tls-client: the string
chrome_154did not produce a correct154 profile, although creating Session did not report a missing profile. Inspect the wire, not just whether the constructor succeeds. - Go tls-client / noble-tls: there is no ready-made154 name, but raw captures confirmed that the stable TLS fields checked in profile 152 matched reference 154, and measured H2 matched as well. The152_PSK variant reproduced resumption, while supported header settings aligned HTTP with 154. This result required no core source changes. The custom ClientProfile/API can extend the configuration, but this test does not prove it can reproduce every future version.
- wreq / primp / rnet: direct selection of 154 is rejected or absent from the enum. An old preset plus a new User-Agent does not provide a full match. Public settings can change some behavior, but the ability to populate a specific missing extension must be checked separately. primp has no public full ClientHello constructor.
- wreq-js / impit: unknown Chrome 154 returns an error. wreq-js offers TLS/H2 settings, but the tested API has no arbitrary trust_anchors payload. impit obtains ready-made profiles from its native catalog.
- CycleTLS: manual JA3/JA4R and H2 are supported, but this Chrome 154 attempt stopped at unsupported extension 51764. A fingerprint string alone cannot build the required ClientHello here.
- uTLS / req: the public ClientHelloSpec and GenericExtension allow extensions to be defined manually, and req provides HTTP/2 configuration. This is a supported route without library changes, but the developer must maintain the custom profile, aligned headers and resumption checks. We do not present a full manual Chrome 154 implementation for this pair as a measured result.
- got-scraping: the header generator accepts a154 version constraint, but measured TLS/H2 still differ. Reaching the current browser transport takes more than generator settings.
- httpcloak: no ready-made
chrome-154exists. We exported the latest profile through describe_preset, registered custom JSON with aligned154 headers through load_preset_from_json, and made four requests. Fresh and resumed JA4, H2 and the TLS fields checked matched. This is a confirmed example of configuration without rewriting the library.
Some libraries therefore do let you assemble a current profile yourself. The difference from a ready-made product is who gathers reference captures, verifies fields, aligns HTTP and handles future updates.
TLS resumption: checking session-resumption behavior
A server can issue a session ticket, after which the client opens a new connection and attempts to resume the TLS session. In TLS1.3 this changes ClientHello: pre_shared_key appears with a real binder and must be the last extension. Other extensions may also disappear. Reusing the same “fresh” template for every connection is not the same as reproducing browser-session behavior. The mechanism is described in RFC 8446, pre_shared_key.
Reference Chrome 154 has the fresh JA4 t13d1517h2_8daaf6152771_cb7bf5808d99, and on resumption t13d1518h2_8daaf6152771_e2d80978ab2e. Extension41 is added, while the basic HTTP/2 summary stays unchanged. BlankTrail produced one fresh request and three accepted resumptions with this transition. Go tls-client152_PSK, noble-tls with that core, wreq Chrome 153, primp in the records obtained, and httpcloak showed the same transition in summary values.
The standard Go tls-client Chrome_152 without the PSK profile produced four fresh handshakes in our run. This does not mean the core cannot resume sessions: switching to supported Chrome_152_PSK changed the result. Capability and the configuration of a particular mode must be assessed separately.

Firefox shows a subtler difference. Reference Firefox 147 adds41 and removes session_ticket 35 on resumption: JA4 still counts17 extensions, while the fresh suffix 3cbfd9057e0d changes to e6dcd7ae0a9e. curl_cffi firefox147 retained35 and added41, giving18 extensions and the suffix 5dff20295fdc. Despite a correct fresh fingerprint, its repeat connection differed from the reference used. BlankTrail Firefox 147 reproduced adding41 and removing35.
Failure to resume in a short run depends on ticket policy, settings and cache. We therefore considered server acceptance of PSK under comparable conditions, not merely the presence of extension 41.
Let’s test how the libraries handle HTTP/3
HTTP/3 runs over QUIC/UDP and was tested separately from TLS/H2: complete ClientHello reconstructed from Initial, transport parameters, Initial/CID, SETTINGS, QPACK and header order. Tests included zero and nonzero QPACK capacity, new connections, PSK and early HTTP requests. The completeness of byte-level analysis is stated for each configuration.
References were installed Chrome 154, Edge 154.0.4258.48 and Firefox 156.0.1 on Windows, Safari 26.6.2 on a MacBook, plus captures from physical Android and iPhone devices.
| Real browser | Fresh QUIC JA4 | Key reference details |
|---|---|---|
| Chrome154 | q13d0312h3_55b375c5d22e_178839b6cec1 | trust_anchors present, Initial 1250 bytes, DCID8 / SCID0. |
| Edge154 | q13d0311h3_55b375c5d22e_653d80c3fe9d | No trust_anchors, Initial 1250, DCID8 / SCID0. Chrome with changed headers does not become Edge. |
| Firefox156 | q13d0315h3_55b375c5d22e_bb76f32061e3 | Its own TLS and QUIC parameter set, Initial 1252, DCID8 / SCID3. |
| Safari 26.6.2 macOS | q13d0311h3_55b375c5d22e_f2a83c8e78ae | Initial 1200, DCID8 / SCID0, H3 SETTINGS: QPACK capacity 16383, blocked streams100 plus GREASE. |
We normalized random GREASE values, key data and connection IDs. trust_anchors was compared by identifiers, not random order, as Root Store can update independently of the browser number. The Chrome QUIC reference does not contain the signature_algorithms GREASE present in its TCP ClientHello: rules from one protocol cannot be applied to another.

How to read this: ✓ - success for the stated criterion, ✕ - a difference or unmet target, △ - partial results or limited confirmation. TLS, TP, Initial/CID, SETTINGS and QPACK are compared with Chrome 154 in the specified configuration, differences for other browser profiles are listed on the right. When H3 does not work, the remaining ✕ indicate unavailable results, not measured defects in every field. For0-RTT rejection, △ means a response without an early-data attempt, so recovery is unconfirmed.
| Solution / mode | H3 works | TLS fields | QUIC TP | Initial / CID | SETTINGS | QPACK | HTTP in 0-RTT | 0-RTT rejection | Defects and test outcome |
|---|---|---|---|---|---|---|---|---|---|
| curl_cffi V3ONLY / chrome150 | ✓ | ✕ | △ | ✕ | ✓ | △ | ✕ | △ | No trust_anchors, Initial 1200 instead of 1250, no early HTTP. Edge 101, Firefox 147 and Safari 260 also show raw differences from the target versions. |
| Go tls-client protocol racing / 152_PSK | ✓ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✓ | Extra TLS extensions, Initial 1280, different CIDs, SETTINGS only 51=1, QPACK encodes ?0 differently. 1+3 instead of 2+2, HTTP is not early.144_PSK improves SETTINGS, not the whole fingerprint. |
| noble-tls native1.16.0 / 152_PSK | ✓ | ✕ | ✕ | ✕ | ✕ | △ | ✕ | ✓ | Different ClientHello, CID and SETTINGS, QPACK has not been separately confirmed in full. Protocol racing duplicates the first GET over H2/H3, HTTP is not early. |
| httpcloak http_version=h3 / latest | ✓ | ✓ | ✓ | ✓ | ✓ | △ | ✕ | ✕ | No defects found in the Chrome TLS/TP/Initial/SETTINGS checked, full QPACK accuracy unproven. Errors after q/qd idle and on 0-RTT rejection. Firefox differs, Safari iOS is limited by its corresponding reference. |
| impit forceHttp3 / chrome151 | ✓ | ✕ | △ | ✕ | ✕ | △ | ✕ | △ | Different ciphers/extensions, Initial 1200, CID20/8, SETTINGS includes MAX_FIELD_SECTION_SIZE=2⁶²−1. PSK works, no early HTTP. Firefox 144 and Chrome with Edge UA also failed to reproduce the target. |
| CycleTLS forceHTTP3 / custom JA3 | ✓ | ✕ | △ | ✕ | △ | △ | ✕ | △ | JA3 does not reproduce QUIC: different extensions, groups, signatures, Initial 1280 and different CIDs. A new fresh connection for each GET, no PSK/0-RTT. |
| req/v3 EnableForceHTTP3 | ✓ | ✕ | △ | △ | ✕ | △ | ✕ | △ | Different TLS groups/signatures, extension 50, SETTINGS6=10485760. New connection without PSK after server closure, no early HTTP. |
| wreq Python HTTP_3 | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | H3 not established: NO_APPLICATION_PROTOCOL. No successful HTTP/3 fingerprint to compare. |
| rnet HTTP_3 | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | H3 not established: UserUnsupportedVersion. |
| wreq-js, primp, Python tls-client, HTTPX, requests, Node fetch, got-scraping Tested packages/APIs | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | No working QUIC in the tested configurations, requests stayed on TCP/H2 or H1. |
| uTLS + Go HTTP/2, Go net/http Tested transports | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | TCP transports, H3 requires another QUIC/HTTP3 client. |
| Windows curl Schannel / --http3-only | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | This build does not support HTTP/3 and rejects the flag. This does not apply to curl_cffi. |
| BlankTrail Proxy 1.3.15 / six profiles | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | No defects found in the fields and scenarios checked.342 QUIC connections in the main series, the current behavior test has72 successful requests and 36 connections. QPACK/GREASE and early HTTP confirmed, live0-RTT rejection tested on Chrome. |
A closer look at raw HTTP/3 bytes
ClientHello was assembled from every CRYPTO fragment in QUIC Initial. The library byte-level sample contains25 connections and 54 header blocks, with TP order, windows, stream limits, Initial and CID preserved.
SETTINGS was saved before conversion to a map, QPACK before decoding into name/value pairs. This preserves the actual order and encoding method, which ordinary JSON logs can hide.
✓ - the fields checked match the stated reference, ✕ - a difference found, △ - limited confirmation, not counted as a full match. Marks apply to a specific profile and sample. In the values below, max means 2⁶²−1.
| Configuration | Reference | TLS fields | QUIC TP | Initial / CID | SETTINGS | QPACK | Specific differences and outcome |
|---|---|---|---|---|---|---|---|
| curl_cffi / Chrome150 | Chrome154 | ✕ | △ | ✕ | ✓ | △ | No trust_anchors51764, Initial 1200 instead of 1250. Stable SETTINGS and their order matched. Full QPACK accuracy unconfirmed. |
| curl_cffi / Safari260 | Safari 26.6.2 Mac | ✕ | △ | ✕ | ✕ | △ | Different groups, key_share and supported_versions. CID20/20 instead of 8/0, SETTINGS6=max →1=0 →7=0 instead of QPACK capacity 16383 and blocked streams100. |
| curl_cffi / Edge101 | Edge154 | ✕ | △ | △ | ✕ | △ | Different TLS, SETTINGS6=max →1=0 →7=0. The outdated preset did not reproduce the current target. |
| curl_cffi / Firefox147 | Firefox156 | ✕ | ✕ | △ | △ | △ | Different ciphers, groups and QUIC parameters. Other fields lack full confirmation. |
| Go tls-client / 152_PSK | Chrome154 | ✕ | ✕ | ✕ | ✕ | ✕ | TCP-like TLS extensions, Initial 1280. SETTINGS only 51=1 instead of 1/6/7/51 + GREASE. QPACK encodes ?0 with a different Huffman flag. |
| Go tls-client / 144_PSK | Chrome154 | ✕ | △ | △ | ✓ | ✕ | SETTINGS and order matched, TLS/QUIC154 did not. QPACK uses a different Huffman flag for ?0. The older preset fixes only some fields. |
| noble-tls / native1.16.0 | Chrome154 / Safari26 | ✕ | △ | ✕ | ✕ | △ | Different ClientHello, CID and SETTINGS. Full wrapper QPACK is not independently confirmed, Go results cannot automatically be applied to it. |
| httpcloak / Firefox-latest | Firefox156 | ✕ | ✕ | △ | △ | △ | Different TLS. TP windows4=15728640 and 5/6/7=6291456 instead of 25165824,12582912,1048576,1048576. |
| httpcloak / Chrome-latest | Chrome154 | ✓ | ✓ | ✓ | ✓ | △ | No defects found in the TLS fields, TP sets, Initial/CID and SETTINGS checked. Full QPACK accuracy unconfirmed, behavioral errors are in the main H3 table. |
| httpcloak / Safari iOS | Limited Mac reference | △ | △ | △ | △ | △ | QUIC windows differ from measured macOS Safari. This byte-level sample had no separate iPhone reference, so this is not a proven iPhone-profile defect. |
| impit / Chrome151 | Chrome154 | ✕ | ✕ | ✕ | ✕ | △ | 15 ciphers instead of 3 TLS1.3 ciphers, TP4=max,5/6/7=1250000 instead of 15728640 and 6291456. Initial 1200, different SETTINGS. |
| CycleTLS / Chrome154 JA3 | Chrome154 | ✕ | ✕ | ✕ | △ | △ | Different ClientHello, TP4=1048576,5/6/7=524288 instead of 15728640 and 6291456. Initial 1280. Custom JA3 did not become an exact QUIC profile. |
| req/v3 / ForceHTTP3 | Chrome154 | ✕ | ✕ | △ | ✕ | △ | Different TLS, including extension 50. TP4=786432, streams_bidi8=0 instead of 100. SETTINGS only 6=10485760 instead of 262144 and 1/7/51/GREASE. |
| BlankTrail / 1.3.15 | Six tested profiles | ✓ | ✓ | ✓ | ✓ | ✓ | No defects found in the fields checked: TLS, normalized TP, Initial/CID, SETTINGS and QPACK match saved references. Safari: fresh/PSK/0-RTT, Initial 1200, Chromium: encoder stream 10. |
A protocol-valid parameter can still differ from a browser: Initial 1200 or a different QUIC window, for example, is not a protocol error, but prevents reproducing the selected reference. This does not prove inevitable antifraud detection, but shows it is potentially possible.
SETTINGS: a concrete mismatch
Chrome154 / Edge154: 1=65536 → 6=262144 → 7=100 → 51=1 → GREASE
Go tls-client / 152_PSK: 51=1
req/v3 / ForceHTTP3: 6=10485760
Missing parameters in 152_PSK cannot be explained by GREASE shuffling. ID/value pairs and their order were compared before conversion to a map. The format is described in RFC9114, and instructions for capturing them yourself are below.
QPACK: identical headers can be encoded differently
In real Chrome 154 and Edge 154, the header sec-ch-ua-mobile: ?0 is sent without Huffman encoding of the value, whereas Go tls-client152_PSK and 144_PSK use it. Both are valid, but the bytes differ from the reference.
Such a comparison requires identical header values and request context. We did not count curl_cffi’s platform difference as a defect because macOS and Windows were being compared. The original54 QPACK blocks were captured at capacity 0. The dynamic table and encoder/decoder streams were tested separately at nonzero capacity under RFC9204.
HTTP/3 behavior was tested with four GETs in one session, separated by2,42 and 2 seconds, with automatic and forced H3 selection: q/qd - no 0-RTT, QPACK capacity 0/65536, qz -0-RTT allowed.
PSK, accepted0-RTT and an early HTTP request are three different results. The pre_shared_key extension only offers resumption. The server separately confirms PSK and early-data acceptance, and we separately check whether the stream carrying the GET itself opened in 0-RTT. For Go tls-client and noble-tls, the server accepted early data, but the GET was sent after the handshake. The used_0rtt flag alone is therefore insufficient.
| Client | Connection selection and lifecycle | HTTP in 0-RTT | 0-RTT rejection | Outcome and limitations |
|---|---|---|---|---|
| BlankTrail Proxy | Chrome/Edge: H3 immediately,2+2 requests. Firefox: TCP first. PSK after idle. | ✓ | ✓ | All6 profiles sent early HTTP.72 requests without errors,36 QUIC connections with no differences in the raw fields checked. |
| curl_cffi | H3 immediately in V3 mode,2+2 requests, PSK after idle. | ✕ | △ | The tested Chrome 150 profile did not offer early data. Raw differences from 154 remain. |
| Go tls-client | H3 immediately,1+3 requests: a new resumed connection for the second GET, then retained through idle. | ✕ | ✓ | Server accepted0-RTT, but the HTTP request was not early. Raw TLS/QUIC and SETTINGS differ. |
| noble-tls | The same1+3 H3 sequence, with the first GET also sent over H2. | ✕ | ✓ | In three modes, the first GET was duplicated over H2 and H3 with protocol_racing=True. No early HTTP, raw QUIC differences remain. |
| httpcloak | auto: all requests over TCP. Forced H3: errors after q/qd idle, qz gives2+2. | ✕ | ✕ | 4 errors in 24 ordinary attempts. Error on 0-RTT rejection, not resolved by two supported retries. |
| impit | H3 immediately with http3=True,2+2 requests, PSK after idle. | ✕ | △ | Early data not offered, raw differences from the selected browser remain. |
| req/v3 | auto: first request over TCP, then H3. Forced: one H3 connection retained through idle. | ✕ | △ | After forced server closure, the new connection was also fresh: no PSK/0-RTT in the tested configuration. |
| CycleTLS | auto: TCP. Forced H3: a new connection for each of four requests. | ✕ | △ | No PSK or early data. Requests succeed, but reuse and raw fields differ from Chrome/Edge. |
In the “0-RTT rejection” column, ✓ means successful delivery after a rejected early-data attempt, ✕ an error, and △ a successful request without attempting0-RTT, so this client’s recovery mechanism was not tested. The server retained ticket keys and PSK capability, closed the previous connection, but disallowed early data. BlankTrail, Go tls-client and noble-tls received responses over resumed connections without accepted0-RTT. httpcloak failed with 0-RTT rejected, and the next request with tls: illegal parameter. Supported retry=2 did not recover the request either.
A duplicate GET was found in noble-tls. In each of three automatic modes, the server handled the same first URL over both HTTP/2 and HTTP/3: it received five requests instead of the four planned. Direct Go tls-client had no duplicate in this series. The result applies to GET and protocol_racing=True, POST behavior was not tested.

In the same fetch scenario, real Chrome and Edge made two fresh H3 requests, then after idle two requests on a new resumed connection, the first being early in qz. BlankTrail with the corresponding profiles repeated that sequence. For Safari and Firefox, initial transport selection depended on saved connections and origin coalescing: Safari retained TCP in q, and used H3 in qd/qz. An initial TCP request is therefore not a universal defect in all browser profiles.
The current BlankTrail run tested Chrome 154 Windows/Android, Edge 154, Firefox 156 and Safari 26 macOS/iPhone. All72 requests completed, and the stable TLS fields checked, transport parameters, Initial/CID, SETTINGS, control streams and QPACK matched across36 QUIC connections. Early HTTP in qz was obtained for all six profiles. The run and 0-RTT rejection test used build 1.4.1064, whose changes are intended for release1.3.15. This confirms specific scenarios, not the frequencies of every permutation or behavior under all network conditions.
Edge and Firefox: testing the current target, not the preset name
In HTTP/2 scenarios, we requested Edge 154 and Firefox 156 using available profiles. curl_cffi edge101 and rnet Edge 134 differ from Edge 154 in TLS, wreq-js Edge 148 is not a ready-made154. primp Edge 151 passed brief detection, but raw signatures and ALPS differ. httpcloak’s Chrome profile with correct Edge headers retained an extra trust_anchors. Firefox 147/148/151 library profiles do not count as full156 matches: raw comparisons show different ciphers/groups. A clean brief JSON does not override that difference.
Also checking Safari, iOS and Android profile quality
A mobile User-Agent does not replace a mobile transport. This comparison covers Safari 26 macOS/iPhone and Chrome 154 Android, not just preset names.
| Client | Safari macOS | Safari iPhone | Chrome Android | Safari: results and differences | Android: results and differences |
|---|---|---|---|---|---|
| curl_cffi | ✕ | ✕ | ✕ | safari260 / safari260_ios: HTTP/2 matched, TLS differed. The iOS preset had session_ticket 35 and padding21, absent from the fresh reference used. | chrome131_android: old TLS class, did not match target 154. |
| wreq Python | △ | △ | △ | Safari 26 and SafariIos26: fresh JA4/H2 matched. No accepted resumption observed in four requests per preset. | Chrome 153 + Platform.Android: JA4/H2 matched class 154, but trust_anchors was empty and Client Hints remained 153. |
| wreq-js | △ | △ | ✕ | safari_26 / safari_ios_26: fresh JA4/H2 matched, standard preset did not resume. A separate attempt to enable PSK through public overrides did not match the reference profile. | chrome_149 + android: HTTP/2 matched, fresh TLS differed. |
| primp | △ | △ | △ | safari_26 + macos/ios: JA4/H2 matched in collected records, resumption observed, raw ClientHello differs in GREASE presence, order and some field contents. Responses incomplete. | chrome_153 + android: summaries matched, but stable TLS fields and Client Hints version still differ. |
| Go tls-client 1.16.0 | ✕ | △ | △ | Direct Go API: safari_ios_26_0 produced matching fresh JA4/H2, no PSK in four requests. safari_16_0 claiming macOS Safari 26 differs in TLS and H2, no ready-made macOS26 in the tested catalog. | chrome_131 with mobile 154 headers: TLS differs, H2 matches. Chrome_152_PSK with aligned Android 154 headers: fresh and resumed JA4/H2 matched, stable TLS fields checked matched the reference transport class used. |
| noble-tls / native 1.16.0 | ✕ | △ | △ | safari_ios_26_0: fresh JA4/H2 matched, no PSK in the short run. Old macOS safari_16_0 did not match target 26. | chrome_131 with mobile UA: fresh TLS did not match. Supported chrome_152_PSK with aligned Android 154 headers produced matching fresh/resumed JA4/H2 and stable TLS fields checked. |
| httpcloak | ✕ | △ | △ | For iOS latest, the TLS summary matched Safari 26, but H2 differed in SETTINGS order, window and pseudo-headers. safari-latest-macos does not exist, this test did not confirm a separate macOS26 profile. | chrome-latest-android: JA4/H2 matched class 154, HTTP headers from original preset 152 need updating. |
| impit | ✕ | ✕ | ✕ | Safari absent from the tested Node package’s Browser enum. | chrome151 with mobile UA: TLS/H2 differed, no separate Android-platform parameter in this test. |
| BlankTrail | ✓ | ✓ | ✓ | Safari_26_macOS and Safari_26_iPhone: TCP/H2 retains the fresh WebKit fingerprint without PSK. In HTTP3,41 appears after idle and JA4 changes as in the Safari 26.6.2 QUIC reference. | Chrome_154_and: fresh and resumed JA4/H2 matched, stable TLS fields checked matched the Chrome 154-class reference used. |
Targets: Safari 26 and Chrome 154 Android. ✓ - matched in the tested configuration, ✕ - target profile not reproduced, △ - partial match or configuration required. JA4/H2 matches without raw-field confirmation do not earn green. Supported manual configuration of Go tls-client and noble-tls matched Android TCP/TLS fields checked, but does not cover the entire emulation, including HTTP/3.
Safari: real26.6.2 did not use PSK in the new TCP connections tested, but resumed QUIC. Absence of TCP resumption is therefore not a defect, whereas PSK in curl_cffi and primp’s Safari scenarios differs from measured behavior.
iOS: BlankTrail’s tested CriOS/FxiOS/EdgiOS paths use WebKit/Safari transport. Requiring desktop Chromium/Gecko ClientHello is incorrect, classifier warnings are separated from client defects.
Android: physical Chrome 154 confirmed PSK, early HTTP in 0-RTT and QPACK encoder stream 10. BlankTrail reproduced the fields and scenarios checked, library-profile limits are in the table.
Detailed Safari/iOS/Android captures, GREASE and QPACK
Safari repeat TCP/H2 connections: one test across all clients
Safari was tested with all 20 solutions, including ordinary transports with a Safari User-Agent where no preset existed. Client and cache were retained between requests, server logs confirmed new connections.
Real Safari 26.6.2 offered no TCP PSK in either safaridriver or a normal window after forced closures. The control Go client resumed on the same server. Absence of Safari TCP resumption is therefore not penalized by itself: it is assessed together with fresh TLS/H2 accuracy. Old Safari 16 without PSK does not become Safari 26.
| Client / available Safari profile | New connections / PSK | Conclusion |
|---|---|---|
| BlankTrail: Safari 26 macOS / iPhone | 4+4, PSK0, accepted0 | Repeat full TLS/H2 handshakes match the selected Safari 26 class, the confirmation gap is closed. |
| Go tls-client: iOS26, macOS16 | 4+4, PSK0 | iOS26: fresh summaries match26, macOS16 retains an outdated TLS profile. |
| noble-tls: iOS26, macOS16 | 4+4, PSK0 | Same core result, no PSK does not fix the old macOS preset. |
| wreq26.4 / wreq-js26.4 | 4+4, PSK0 | Fresh Safari summaries and absence of PSK match selected behavior, without removing other raw-data limitations. |
| httpcloak Safari iOS | 4, PSK0 | Repeat full handshake, measured H2 difference from Safari remains. |
| rnet Safari18.5 | 4, PSK0 | Fresh summary matches class 26, preset age and raw differences remain. |
| curl_cffi Safari260 | 4, PSK offered/accepted3 | Fresh TLS already differs, three repeat ClientHello resumed, unlike the selected Safari 26.6.2. |
| primp Safari26 | 2 connections from 4 attempts, PSK1 accepted | Repeat ClientHello contains41 and the server accepted resumption, two other responses were lost on H2 closure. The incomplete-run count is disclosed. |
| Python tls-client Safari15.6.1 | 4, PSK0 | Outdated fresh TLS does not match26. |
| impit, CycleTLS, req/v3, uTLS+GoH2, got-scraping | 4 responses each, exact Safari 26 not obtained | Tested fallback/ordinary transports do not replace a missing current Safari preset. PSK in nonbrowser transport is not treated as a separate Safari-profile defect. |
| requests, HTTPX, Go net/http, Windows curl, Node fetch | 4 attempts each, HTTPX received2 responses | Controls with Safari User-Agent, Safari emulation neither claimed nor obtained. |
iOS: browser name and transport engine
For BlankTrail1.3.15, we measured database filters chrome+ios, firefox+ios, edge+ios and safari+ios, plus User-Agent selection for CriOS, FxiOS, EdgiOS and EdgiOS on iPad. All eight paths produced one WebKit/Safari transport class. Different UA brands and versions do not mean an iOS profile should send desktop Chromium or Gecko ClientHello.
| Profile-selection path | New TCP connections | Measured TLS/H2 | PSK41 |
|---|---|---|---|
| database chrome+ios / firefox+ios / edge+ios / safari+ios | 3 each,12 total | Safari JA4 t13d2013h2_a09f3c656075_7f0f34a4126d, H2 m,s,a,p | Absent in all 12 |
| By UA: CriOS / FxiOS / EdgiOS / EdgiOS iPad | 3 each,12 total | Same Safari JA4 and H2 | Absent in all 12 |
| Chrome Windows control | 3 | Fresh t13d1517h2…cb7bf5808d99 → PSK t13d1518h2…e2d80978ab2e, Chromium H2 m,a,s,p | Present in two repeat ClientHello records |
All24 TCP records across eight iOS paths produced H2 2:0;3:100;4:2097152;9:1|10420225|0|m,s,a,p. UAs contained the appropriate CriOS/FxiOS/EdgiOS tokens. This tests the product’s profile selection, not24 physical iPhone/iPad runs.
In the current iOS run, research /client received24 responses:21 with CriOS/FxiOS/EdgiOS contained engine_mismatch, and CriOS/EdgiOS also had client_hints_absent. On the public /client , eight scenarios were tested separately: seven reproduced those false warnings, Safari passed. Server records show expected WebKit/Safari TLS/H2. Demanding desktop Chromium/Gecko and desktop Client Hints here is a detector-classification error, not an identified product defect.
Safari HTTP/3: fresh and PSK forms need separate references
Unlike TCP, the saved Safari 26.6.2 QUIC reference contains a fresh form and two resumption variants, with and without 0-RTT. BlankTrail reproduced these states while retaining cache after idle.
| Scenario | QUIC JA4 | Extensions | Initial |
|---|---|---|---|
| Live Safari 26.6.2: fresh | q13d0311h3_55b375c5d22e_f2a83c8e78ae | No41/42 | 1200 |
| Live Safari: PSK without 0-RTT | q13d0312h3_55b375c5d22e_151122171f7d | 41 last, no 42 | 1200 |
| Live Safari: PSK with 0-RTT allowed | q13d0313h3_55b375c5d22e_6bb9a3ac9a4b | 42 between45 and 43,41 last | 1200 |
| BlankTrail Safari 26 macOS / iPhone | Each profile: fresh…f2a83c8e78ae → PSK…151122171f7d | Order,41 last and absence of 42 matched the chosen reference variant | 1200 in all 4 QUIC connections |
For Safari macOS/iPhone, the ciphers, groups, signature algorithms, versions, key_share form, stable extension payloads and TP values compared matched QUIC references after random-data normalization.
Fresh wire order:
GREASE,0,10,16,5,13,18,51,45,43,57,27,GREASE
PSK without 0-RTT:
GREASE,0,10,16,5,13,18,51,45,43,57,27,GREASE,41
Observed transport parameter orders:
14 15 4 5 6 7 9
9 14 15 4 5 6 7
4 5 6 7 9 14 15
Safari TP order uses cyclic rotations of 4,5,6,7,9,14,15, not arbitrary shuffling. Every product order captured belongs to this family.
Safari macOS and iPhone confirmed not only tls_resumed and used_0rtt, but also the HTTP request bytes in early data. With delayed server responses, Early-Data:1 was the last header of the repeat request and absent from the fresh request.
In the current BlankTrail run, all 342 QUIC connections across six profiles passed the listed checks of stable TLS fields, TP, SETTINGS, Initial/CID and QPACK streams. Chromium sends12583 after accumulating origin RTT, Firefox retains fixed0xff02de1a (min_ack_delay), which must not be treated as random GREASE.
H3 measurement conditions:
h2_spoofing, enable_http3 and spoof_headers enabled, explicit profile, UA taken from the catalog. The original client passes context through Sec-Fetch-Dest/Mode/Site.
The first QPACK block depends on when SETTINGS arrives. In six control runs of real Chrome 154.0.8037.95, the first block was dynamic, and NetLog confirmed SETTINGS before headers. A dynamic first block from the product is therefore not a defect by itself.
HTTP/3 structure, GREASE and QPACK across six profiles
Shuffling was tested on 300 fresh HTTP/3 connections:50 each for Chrome 154 Windows/Android, Edge 154, Firefox 156 and Safari 26 macOS/iPhone. Each profile used server QPACK capacity 0 for 25 connections and capacity 65536 for 25.
| Item checked | Chrome / Edge | Firefox | Safari macOS / iPhone |
|---|---|---|---|
| Stable TLS fields and SETTINGS | Matched in all 100 connections | Matched in all 50 connections | Matched in all 100 connections, macOS compared with a separate reference |
| Initial and CID | 1250/1250, DCID8, SCID0 | 1252/1252, variable DCID, SCID3 | 1200/1200, DCID8, SCID0 |
| TP and TLS ordering | 50 distinct TP orders each, GREASE and QUIC version order vary | Fixed TP, TLS shuffled with 57,65037 at the end | All three permitted TP rotations, no other orders |
| Frames after SETTINGS | GREASE payload0–3 bytes and PRIORITY_UPDATE | GREASE payload0–7 bytes, no PRIORITY_UPDATE | No extra frames |
| QPACK at capacity 0 | Static block, control stream only | Static block, control and both QPACK streams | Static block, control stream only |
| QPACK at capacity 65536 | Insertions, dynamic references, encoder stream 10 | Insertions and both QPACK streams | Encoder sends server capacity, request stays static |
Chromium produced50 distinct TLS and TP orders per profile and both available_versions orders. Firefox had50 TLS orders with fixed TP, Safari had fixed TLS and three reference TP orders. All300 fresh connections passed structural checks. PSK and early HTTP were tested on repeat connections.
For Edge and Firefox, eight two-sample KS tests using saved100 and 120 browser connections at a0.01 significance level did not reject equality of individual distributions, minimum p=0.029. This does not prove equality of the joint distribution. The current run checks structure and variability without repeating those KS calculations.
Chrome 154 Android: comparison with a physical device
Physical Android 154 captures include fresh and resumed q/qd/qz: q/qd accepted PSK without 0-RTT, qz accepted PSK and 0-RTT with confirmed early HTTP.
Android and BlankTrail matched on the TLS fields checked, normalized TP, stable SETTINGS and header order. Initial 1250/1250, DCID8, SCID0.
| Scenario | Real Android Chrome 154 | BlankTrail1.3.15 |
|---|---|---|
| qd: dynamic table | Control stream 2, QPACK encoder stream 10 | Control stream 2, QPACK encoder stream 10 - matched |
| qz: static form | Control stream 2 only | Control stream 2 only - matched |
| qz: repeat after idle | PSK/0-RTT accepted, request in 0-RTT | PSK/0-RTT accepted, request in 0-RTT - matched |
Chromium encoder stream 10 is confirmed by real desktop Chrome and Android. BlankTrail reproduced it for Chrome Windows, Edge Windows and Chrome Android in 25 fresh dynamic-table connections per profile and in repeat connections.
BlankTrail: browser transport for legacy and third-party software
Linux curl7.74.0 through BlankTrail produced the checked Chrome 154, Edge 154, Firefox 156 and Safari 26 profiles. The old client was unchanged: the proxy builds the selected TLS/H2/H3 transport.

Advantage: connect different stacks and third-party software without source access or native-library integration. Drawback: a separate application to install, run and maintain. For HTTPS impersonation, the client must trust the CA, certificate pinning can prevent operation.
The article uses version 1.3.15. BlankTrail’s stated policy is to update the database no later than five days after a stable browser release. This study checks the listed sample from the catalog’s841 profiles, while the entire catalog was captured and tested against corresponding real browser versions on physical devices and the required OSes.
TCP test parameters and measured JA4/H2
Tested with Linux curl7.74.0 and OpenSSL: the same client through BlankTrail produced Chrome 154, Edge 154, Firefox 156, Safari 26 and earlier profiles. Each profile used a new test port with an empty TLS cache. H2 spoofing and session resumption were enabled, HTTP/3 and browser_mode disabled. After changing profiles, the test restarted, tickets from the previous profile were not used as a “fresh” connection.
Below are measured fingerprints for three BlankTrail profiles. HTTP/2 is written in Akamai format: SETTINGS in send order, connection WINDOW_UPDATE, priority summary and pseudo-header order. The symbols m, a, s, p mean :method, :authority, :scheme, :path. Chrome/Firefox retain HTTP2 after TLS resumption, Safari after another full handshake.
The catalog contains841 profiles. Live tests cover the listed sample, not the whole catalog.
Chrome_154_win
fresh: t13d1517h2_8daaf6152771_cb7bf5808d99
resumed: t13d1518h2_8daaf6152771_e2d80978ab2e
HTTP/2: 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p
Firefox_156_win
fresh: t13d1517h2_8daaf6152771_3cbfd9057e0d
resumed: t13d1517h2_8daaf6152771_e6dcd7ae0a9e
HTTP/2: 1:65536;2:0;4:131072;5:16384|12517377|0|m,p,a,s
Safari_26_macOS
fresh: t13d2013h2_a09f3c656075_7f0f34a4126d
repeat: t13d2013h2_a09f3c656075_7f0f34a4126d (full handshake; no PSK)
HTTP/2: 2:0;3:100;4:2097152;9:1|10420225|0|m,s,a,p
How to repeat the comparison
- Capture the real target browser and the client under test at tls.peet.ws/api/all, and save versions, profile, OS and JSON.
- Get brief detection at BlankTrail /client. It helps identify contradictions, but does not establish byte-for-byte equality.
- Compare the actual protocol, TLS fields, SETTINGS and headers. For matching JA4, inspect extension payloads, including trust_anchors.
- Test new connections with the session cache retained. For H3, distinguish PSK, accepted0-RTT and the early HTTP request itself.
Detailed instructions: pcap, session keys, QPACK and 0-RTT rejection
BlankTrail /client provides a brief result: recognized popular client, claimed browser and detected contradictions. Save JSON, client version, preset, OS and date. This is a convenient first step, no findings does not prove all bytes match.
- Get an independent detailed result. Open tls.peet.ws/api/all with the real target browser, then request the same URL using the library. Save both JSON results. The service is described in the TrackMe repository, /api/all returns collected TLS and HTTP fields. Do not compare browser navigation with a library fetch without accounting for HTTP-header context.
- Start with the transport. Check http_version and negotiated ALPN. For TLS, compare ciphers, extensions, supported_versions, supported_groups, signature_algorithms, key_share, ALPS and compression. Use JA3/JA4/PeetPrint as summaries, inspecting extension contents separately, including trust_anchors, when hashes match. For HTTP2, compare akamai_fingerprint and sent_frames: SETTINGS, WINDOW_UPDATE, pseudo-headers and regular headers. Available fields depend on the protocol actually used.
- Do not treat expected randomness as a defect. Normalize GREASE values while preserving counts and positions. For Chrome/Edge, capture several new connections and check shuffling. random, session ID, key_share key bytes, tickets and binders should not repeat. Compare key groups and lengths, and trust_anchors against the allowed ID set for the Root Store version. Check reference-fixed order separately.
- Capture traffic yourself for actual raw bytes. /api/all provides parsed fields, not a full pcap for every request. Capture your client’s TCP443 and UDP443 in Wireshark. TCP ClientHello is parsed from TLS records and handshakes, QUIC ClientHello must be reconstructed from Initial CRYPTO fragments, not one UDP packet treated as complete TLS. HTTP2/HTTP3 require session keys: enable key logging in a client that supports it, then configure the file in Wireshark’s TLS settings. Instructions are in the Wireshark TLS documentation, QUIC fields in the dissector reference. Public JSON does not replace this step.
- Repeat with fresh and new connections in the same session. Keep the client session cache, wait for the ticket, and close the connection on your server while retaining ticket keys. A new request on old H2/H3 is not resumption. PSK41 in ClientHello is an offer, confirm acceptance through the selected PSK identity in ServerHello or server DidResume. For Safari 26.6.2, separate TCP/H2 from QUIC/H3. In the chosen TCP experiment, new connections had no 41. The QUIC reference resumes: without 0-RTT only 41 is added, with 0-RTT allowed42 is also added. Compare the corresponding protocol and server policy.
- Test HTTP3 separately. Enable supported H3 mode, use Alt-Svc/HTTPS-record advertisement and verify the response actually used H3. For independent public analysis, try tls3.peet.ws/api/all, but the URL alone does not guarantee QUIC, check actual http_version. In pcap compare TLS, complete transport-parameter payloads, first Initial size, CID, SETTINGS and header order. After decryption, save SETTINGS type4 before converting to a map: compare ID/value order and GREASE separately. For HEADERS type1, parse the QPACK prefix and instructions first, then compare decoded values. For equal values, compare static indices, literal/name-reference and Huffman/Never Index. Test QPACK dynamics separately with nonzero server capacity, multiple requests and encoder/decoder streams. Do not automatically apply TCP GREASE rules to QUIC.
- Measure HTTP/3 behavior per connection. Send four safe GETs using one client, separated by2,42 and 2 seconds. On your server save connection ID, protocol, stream ID and each request’s time. Repeat with capacity 0/65536 and 0-RTT allowed/disallowed. To reject early data, retain ticket keys and PSK but prohibit early data, then verify delivery exactly once after the handshake. Separately record early_data offer, server acceptance and HTTP-stream opening in 0-RTT. Public /client gives brief detection, this timing sequence needs pcap with session keys or your HTTP/3 server logs.
curl_cffi example: the first URL provides detailed fields, the second brief detection. We use available chrome150 here, the name chrome does not promise154 support.
from curl_cffi import requests
with requests.Session(impersonate="chrome150") as session:
for url in ("https://tls.peet.ws/api/all",
"https://audit.blanktrail.com/client"):
response = session.get(url, timeout=20)
print(response.json())
For an old client through BlankTrail, configure a port with the required profile and appropriate CA, compare H2 and H3 separately. Example:
curl --proxy http://127.0.0.1:PROXY_PORT \
--cacert /path/to/blanktrail-ca.crt \
https://tls.peet.ws/api/all

The illustration preserves real brief /client responses from TCP checks, without IPs or one-time names. Their green result applies to TCP and does not confirm HTTP/3 matching.
Which solutions came out ahead
BlankTrail ranks first for tested coverage of current targets and ready-made profiles. Among libraries, httpcloak is closest to Chrome QUIC in the raw fields checked, but fails after idle and on 0-RTT rejection. Go tls-client and noble-tls accurately reproduced tested Chrome TCP/H2, but have QUIC/QPACK limitations, noble-tls also duplicated a GET. In wreq/primp, matching JA4 hides empty trust_anchors. Other clients’ older presets retain the listed TLS/H2/H3 defects.
Ranks consider target-profile accuracy, browser coverage, HTTP/3 and maintenance convenience. This ranks tested configurations, not antifraud detection probability.
Limits of result confirmation
Confirmation limits and iOS classification
For current Firefox 156, the saved raw TCP reference is fresh. On resumption the product adds41 and removes35, but there is no full paired capture of resumed Firefox 156 yet: the transition is neither called a proven defect nor presented as a full raw reference comparison. Safari TCP confirms ciphers, extensions, signature algorithms, H2 and no PSK, but not every extension payload has a saved paired browser reference. Edge accepted QUIC0-RTT in all four qz repeats, with early HTTP in two.0-RTT acceptance does not mean every HTTP request precedes handshake completion, comparing frequencies needs an equivalent real-Edge series.
Ranking all 20 clients
Priority: Chrome 154 accuracy, then Safari/Firefox/Edge/Android and HTTP/3, then ready-made profiles and integration. ✓ - criterion confirmed, △ - partial or limited, ✕ - absent capability or unmet target. Explanations are in tooltips and client cards. Green Chrome refers to TCP/H2, HTTP/3 is assessed separately.
Fingerprint test results
| № | Client | Chrome154 | Safari26 | Firefox156 | Edge154 | Android154 | HTTP/3 | HTTP/2 | trust_anchors | Client Hints | GREASE | Repeat TLS |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | BlankTrail Proxy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 2 | httpcloak | ✓ | △ | ✕ | ✕ | △ | △ | △ | ✓ | △ | △ | ✓ |
| 3 | Go tls-client | ✓ | △ | ✕ | ✕ | △ | ✕ | △ | ✓ | △ | △ | △ |
| 4 | noble-tls | ✓ | △ | ✕ | ✕ | △ | ✕ | △ | ✓ | △ | △ | △ |
| 5 | wreq Python | △ | △ | ✕ | ✕ | △ | ✕ | ✓ | ✕ | ✕ | △ | ✓ |
| 6 | primp | △ | △ | ✕ | ✕ | △ | ✕ | ✓ | ✕ | ✕ | ✕ | ✕ |
| 7 | wreq-js | ✕ | △ | ✕ | ✕ | ✕ | ✕ | ✓ | ✕ | ✕ | △ | △ |
| 8 | curl_cffi / curl-impersonate | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✓ | ✕ | △ | ✕ | ✕ |
| 9 | rnet | ✕ | △ | ✕ | ✕ | ✕ | ✕ | ✓ | ✕ | ✕ | ✕ | △ |
| 10 | Python tls-client | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✓ | ✕ | △ | ✕ | △ |
| 11 | req/v3 | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | △ | ✕ | △ | ✕ | ✕ |
| 12 | uTLS + Go HTTP/2 | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✓ | △ | △ | △ |
| 13 | impit | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | △ | ✕ | ✕ |
| 14 | CycleTLS | ✕ | △ | ✕ | ✕ | ✕ | ✕ | △ | ✕ | △ | ✕ | △ |
| 15 | got-scraping | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✓ | ✕ | ✕ |
| 16 | HTTPX | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | △ | ✕ | △ | ✕ | ✕ |
| 17 | Go net/http | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | △ | ✕ | △ | ✕ | △ |
| 18 | Windows curl | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | △ | ✕ | ✕ |
| 19 | requests | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | △ | ✕ | ✕ |
| 20 | Node fetch | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | ✕ | △ | ✕ | △ |
Integration, profiles and maintenance
| № | Client | Integration | Profiles | Updates | Stack |
|---|---|---|---|---|---|
| 1 | BlankTrail Proxy | ✓ | ✓ | ✓ | ✓ |
| 2 | httpcloak | △ | △ | △ | △ |
| 3 | Go tls-client | △ | △ | △ | △ |
| 4 | noble-tls | ✓ | △ | △ | △ |
| 5 | wreq Python | ✓ | ✕ | △ | △ |
| 6 | primp | ✓ | ✕ | △ | △ |
| 7 | wreq-js | ✓ | ✕ | △ | △ |
| 8 | curl_cffi / curl-impersonate | ✓ | ✕ | △ | △ |
| 9 | rnet | ✓ | ✕ | △ | △ |
| 10 | Python tls-client | ✓ | ✕ | △ | △ |
| 11 | req/v3 | △ | △ | △ | △ |
| 12 | uTLS + Go HTTP/2 | ✕ | △ | ✕ | △ |
| 13 | impit | ✓ | ✕ | △ | △ |
| 14 | CycleTLS | △ | △ | ✕ | △ |
| 15 | got-scraping | ✓ | ✕ | ✕ | △ |
| 16 | HTTPX | ✓ | ✕ | ✕ | △ |
| 17 | Go net/http | ✓ | ✕ | ✕ | △ |
| 18 | Windows curl | ✓ | ✕ | ✕ | △ |
| 19 | requests | ✓ | ✕ | ✕ | △ |
| 20 | Node fetch | ✓ | ✕ | ✕ | △ |
Strengths, limitations and verdict for each client
Expand a card for versions, selected profiles and explanations of every rating. Client order and ranks are identical in both tables.
1. BlankTrail Proxy 1.3.15
Strengths: Ready-made coverage of current targets, connects legacy/third-party software through a proxy without code changes, tested Android H3 and Chromium QPACK fields match references
Weaknesses / limitations: Standalone application: requires a separate installation, offset by cross-platform support and Docker deployment.
Verdict: First for ready-made coverage of current targets and proxy compatibility with third-party software. Tested H3/QPACK fields match references, separate installation and stated confirmation limits remain. HTTP/3:72 error-free requests,36 connections with no differences in checked fields, early GET and 0-RTT rejection tested.
- ✓ Chrome154
- H2:154 fields
- ✓ Safari26 mac/iOS
- TCP Safari 26 and database/UA CriOS/FxiOS/EdgiOS: WebKit H2,24 iOS connections without PSK
- ✓ Firefox156
- Fresh Firefox 156 TCP/H2 and checked H3 fields matched, no full paired raw resumed TCP reference 156
- ✓ Edge154
- Edge 154 TCP/H2 and checked H3 fields, including Chromium encoder stream 10, matched
- ✓ Chrome Android154
- New physical Android 154: checked TLS/TP/SETTINGS/Initial and QPACK stream 10 matched, PSK and early qz request confirmed
- ✓ HTTP/3
- 342 QUIC connections across six profiles with no differences in the listed stable fields,300 fresh for GREASE/QPACK,42 for fresh/resumed, not universal byte equality
- ✓ HTTP/2
- Target profiles
- ✓ trust_anchors
- Chrome: ID set
- ✓ Client Hints
- Desktop Client Hints aligned, iOS WebKit without desktop CH. Requiring CH from CriOS/EdgiOS in current /client is a detector-classification error
- ✓ GREASE / shuffling
- Structure, variability and shuffling confirmed by the series, no universal statistical equivalence claimed
- ✓ Resumption
- Safari TCP without PSK, H3 PSK and early HTTP confirmed for all six profiles. Edge: QUIC0-RTT4/4, early HTTP2/4, no full resumed TCP reference for Firefox 156
- ✓ Integration
- Connect through HTTP/SOCKS proxy without embedding a library in application source. The corresponding HTTPS mode requires CA trust configuration.
- ✓ Required profiles
- Ready-made154/156/26
- ✓ Keeping profiles current
- Product database, within five days of release
- ✓ Code / stack
- Works through a proxy with different stacks, legacy and third-party software without source access. Compatibility depends on the required proxy mode and CA trust.
2. httpcloak1.7.2
Strengths: Best confirmed Chrome QUIC among libraries in these tests
Weaknesses / limitations: No exact set of all current targets, custom maintenance. H3: auto stayed on TCP, errors after q/qd idle and on 0-RTT rejection, retry=2 did not help.
Verdict: Above Go/noble for Chrome H3, below BlankTrail for ready-made coverage
- ✓ Chrome154
- custom154/H2
- △ Safari26 mac/iOS
- iOS: TLS summary, different H2, no Mac preset
- ✕ Firefox156
- Latest is not156, H3 differs
- ✕ Edge154
- Chrome with Edge UA: extra51764
- △ Chrome Android154
- H2 latest after CH154
- △ HTTP/3
- Chrome: checked fields matched, FF/Safari differ
- △ HTTP/2
- Chrome yes, Safari no
- ✓ trust_anchors
- Chrome: ID set
- △ Client Hints
- Aligned in custom, other targets manual
- △ GREASE / shuffling
- Chrome: observed, not all targets
- ✓ Resumption
- Chrome PSK accepted, Safari repeats without PSK
- △ Integration
- JSON configuration and API
- △ Required profiles
- 154 manual, latest152
- △ Keeping profiles current
- Project updates presets, custom profiles are your responsibility
- △ Code / stack
- Several SDKs, integration required
3. Go tls-client1.16.0
Strengths: TLS/H2 constructors, accurate tested Chrome H2
Weaknesses / limitations: Mac Safari 16, current Edge/Firefox, QUIC/SETTINGS, QPACK Huffman for ?0. H3: resumes on second request, no early GET,0-RTT rejection handled.
Verdict: Strong H2 constructor, H3 lowers the overall result
- ✓ Chrome154
- 152_PSK:154 H2 fields
- △ Safari26 mac/iOS
- iOS26 fresh H2, mac16 outdated
- ✕ Firefox156
- 148 is not156
- ✕ Edge154
- Chrome profile is not Edge 154
- △ Chrome Android154
- 152_PSK/H2 with Android CH154
- ✕ HTTP/3
- QUIC TLS/CID,152_PSK SETTINGS only 51, QPACK ?0 encoded differently
- △ HTTP/2
- Chrome/iOS yes, mac16 no
- ✓ trust_anchors
- Chrome H2: ID set
- △ Client Hints
- Set by calling code
- △ GREASE / shuffling
- Chrome TCP yes, QUIC differs
- △ Resumption
- Chrome yes, iOS26 without PSK, mac16 outdated
- △ Integration
- Go API, code required
- △ Required profiles
- No154, profile/headers manual
- △ Keeping profiles current
- Core releases + custom maintenance
- △ Code / stack
- Go, other languages through wrappers
4. noble-tls0.1.9 / native1.16.0
Strengths: Convenient async with Go tls-client core
Weaknesses / limitations: H3 differences, separate wrapper/core versions. H3: first GET duplicated over H2/H3 in protocol racing, no early GET,0-RTT rejection handled.
Verdict: More maintenance than the direct Go API
- ✓ Chrome154
- 152_PSK:154 H2 fields
- △ Safari26 mac/iOS
- iOS26/H2, mac16 differs
- ✕ Firefox156
- 148 is not156
- ✕ Edge154
- Chrome profile is not Edge 154
- △ Chrome Android154
- 152_PSK/H2 with CH154
- ✕ HTTP/3
- QUIC differs, wrapper raw QPACK not captured separately
- △ HTTP/2
- Chrome/iOS yes
- ✓ trust_anchors
- Chrome H2: ID set
- △ Client Hints
- Set by Python code
- △ GREASE / shuffling
- TCP yes, QUIC differs
- △ Resumption
- Chrome yes, iOS26 without PSK, mac16 outdated
- ✓ Integration
- Python async API
- △ Required profiles
- New names through native core, enum lags behind
- △ Keeping profiles current
- Update package and native core separately
- △ Code / stack
- Python + native binaries
5. wreq Python0.12.3
Strengths: Good Safari H2 presets, platform selection
Weaknesses / limitations: Empty trust anchors, old CH, H3 did not work
Verdict: Above primp for checked Chrome TLS fields
- △ Chrome154
- 153: JA4/H2 yes, raw defects
- △ Safari26 mac/iOS
- 26 mac/iOS H2 summaries
- ✕ Firefox156
- 151 is not156
- ✕ Edge154
- 148 is not154
- △ Chrome Android154
- 153: H2 summaries
- ✕ HTTP/3
- HTTP_3: NO_APPLICATION_PROTOCOL
- ✓ HTTP/2
- Chrome/Safari summaries
- ✕ trust_anchors
- Empty51764
- ✕ Client Hints
- 153 with UA154
- △ GREASE / shuffling
- sigGREASE/shuffling, incomplete raw coverage of all targets
- ✓ Resumption
- Chrome PSK, Safari 26 repeats without PSK
- ✓ Integration
- Python sync/async
- ✕ Required profiles
- No ready-made154
- △ Keeping profiles current
- wreq releases, update dependency
- △ Code / stack
- Python/Rust, separate Node package
6. primp2.0.1 (Python)
Strengths: Simple API, broad preset selection
Weaknesses / limitations: trust anchors/ALPS/GREASE, different Safari PSK, H2-closure errors
Verdict: Convenience does not compensate for raw differences
- △ Chrome154
- 153: summaries match, raw differences
- △ Safari26 mac/iOS
- 26: summaries match, raw differences
- ✕ Firefox156
- 151 is not156
- ✕ Edge154
- 151: raw signatures/ALPS differ
- △ Chrome Android154
- 153: H2 summaries
- ✕ HTTP/3
- The tested Python primp2.0.1 package was built without the http3 feature and offers no QUIC/H3 API. The Rust core has a separate optional http3 feature, but that is another build, whose results cannot be applied to the Python package.
- ✓ HTTP/2
- Summaries obtained
- ✕ trust_anchors
- Empty51764
- ✕ Client Hints
- 153 with UA154
- ✕ GREASE / shuffling
- No GREASE in Chrome sigalgs
- ✕ Resumption
- Safari 26: PSK1 accepted,2/4 responses lost
- ✓ Integration
- Python sync/async
- ✕ Required profiles
- No154
- △ Keeping profiles current
- Package releases, limited custom configuration
- △ Code / stack
- Python/Rust transport
7. wreq-js3.2.0
Strengths: Safari H2 and familiar fetch
Weaknesses / limitations: Old Chrome/Edge, exact154 not obtained
Verdict: Below newer Python presets
- ✕ Chrome154
- 149: different TLS
- △ Safari26 mac/iOS
- 26 mac/iOS H2 summaries
- ✕ Firefox156
- 151 is not156
- ✕ Edge154
- 148 is not154
- ✕ Chrome Android154
- 149: different TLS
- ✕ HTTP/3
- wreq-js3.2.0 and its wreq0.16.1 have no supported QUIC/H3 transport. HTTP3 ALPN in TLS configuration types does not create a QUIC transport, auto requests used TCP/H2.
- ✓ HTTP/2
- Chrome/Safari summaries
- ✕ trust_anchors
- Required payload missing
- ✕ Client Hints
- Old preset, manual setup
- △ GREASE / shuffling
- Overrides available, current154 not obtained
- △ Resumption
- Safari 26 without PSK, exact Chrome 154 not obtained
- ✓ Integration
- Fetch API Node/Bun
- ✕ Required profiles
- 154 rejected
- △ Keeping profiles current
- npm/core releases
- △ Code / stack
- Node/Bun, code integration
8. curl_cffi0.16.3 / curl-impersonate
Strengths: Broad API, working H3, low integration cost
Weaknesses / limitations: Current targets mismatch at TLS/QUIC. H3 PSK works after idle, but the tested profile did not offer early data.
Verdict: H3 and flexibility are useful, exact154 not obtained
- ✕ Chrome154
- 150: no 51764/sigGREASE
- ✕ Safari26 mac/iOS
- Safari 260: raw/QUIC differences
- ✕ Firefox156
- 147: not156, old resumed defect
- ✕ Edge154
- Edge 101 outdated
- ✕ Chrome Android154
- Android 131 outdated
- ✕ HTTP/3
- Chrome SETTINGS matched, TLS/Initial 154 and Safari raw fields differ
- ✓ HTTP/2
- Chrome H2 matched
- ✕ trust_anchors
- No51764
- △ Client Hints
- Manual headers / presets
- ✕ GREASE / shuffling
- No target 154 sigGREASE
- ✕ Resumption
- Safari:3 PSKs accepted, Firefox resumed retains35
- ✓ Integration
- Requests API sync/async
- ✕ Required profiles
- No154, custom attempt did not match
- △ Keeping profiles current
- Library releases, maintain custom yourself
- △ Code / stack
- Python/libcurl/CLI ecosystem
9. rnet2.4.2
Strengths: Ready-made presets and sync/async
Weaknesses / limitations: Old catalog, H3 API rejected request
Verdict: Profile availability limits the result
- ✕ Chrome154
- 137 outdated
- △ Safari26 mac/iOS
- 18.5:26 summary, raw differences
- ✕ Firefox156
- 139 is not156
- ✕ Edge154
- 134 is not154
- ✕ Chrome Android154
- 137 with Android UA differs
- ✕ HTTP/3
- HTTP_3: UnsupportedVersion
- ✓ HTTP/2
- Chrome/Safari summaries
- ✕ trust_anchors
- No51764
- ✕ Client Hints
- Old preset CH
- ✕ GREASE / shuffling
- Old sigalgs/ALPS
- △ Resumption
- Safari repeats without PSK, full current profile unconfirmed
- ✓ Integration
- Python sync/async
- ✕ Required profiles
- No154
- △ Keeping profiles current
- Package updates
- △ Code / stack
- Python/Rust
10. Python tls-client1.0.1
Strengths: Easy to get started
Weaknesses / limitations: Core and catalog significantly behind
Verdict: Convenient API with old transport
- ✕ Chrome154
- 120 outdated
- ✕ Safari26 mac/iOS
- Safari 15.6 outdated
- ✕ Firefox156
- Four requests with firefox_120 and Firefox 156 UA. Raw groups:29,23,24,25,256,257, reference 156 has4588 and a different set. No ready-made156 in catalog 1.0.1.
- ✕ Edge154
- No ready-made Edge 154 in catalog 1.0.1. Tested Chromium chrome_120 with Edge 154 UA: groups lack4588, old ALPS17513, no reference GREASE in signature_algorithms.
- ✕ Chrome Android154
- 120 with Android UA differs
- ✕ HTTP/3
- Installed Python wrapper1.0.1 and its bundled native transport have no supported HTTP/3 API, no QUIC connections obtained.
- ✓ HTTP/2
- Chrome H2 summary
- ✕ trust_anchors
- No51764
- △ Client Hints
- Headers set manually
- ✕ GREASE / shuffling
- Old groups/ALPS
- △ Resumption
- Safari 15.6 without PSK, but fresh TLS outdated
- ✓ Integration
- Python, requests-like
- ✕ Required profiles
- 154 name does not produce a correct profile
- △ Keeping profiles current
- Native core and package need updating
- △ Code / stack
- Python/native
Details of supported configuration tested
Python tls-client1.0.1: firefox_120 with UA156 retained old groups without 4588, the Chromium profile with Edge 154 UA retained old ALPS and signature algorithms. No ready-made target versions. req/v3: ImpersonateSafari and ImpersonateFirefox use old Safari 16/Firefox 120 profiles, Chrome preset is120. uTLS1.8.2: Auto selects Chrome 133, Firefox 120, Safari 16 and Edge 85. Their new TCP connections did not match current targets. Custom ClientHelloSpec can build another configuration, but does not make these ready-made profiles current. Versions and presets can be checked in API req/v3 and API uTLS1.8.2.
Details of supported configuration tested
HTTP/3 is assessed for the specific distribution. Tested Python primp2.0.1 has no QUIC: its Python build omits the Rust core’s optional http3 feature. In wreq-js3.2.0, Python tls-client1.0.1, got-scraping4.2.1 and ordinary control transports, HTTP/3 is absent. uTLS + Go HTTP/2 also has no QUIC, adding a separate QUIC client would test a different solution.
11. req/v3 3.61.0
Strengths: Flexible TLS/H2 API and working H3
Weaknesses / limitations: Built-in Chrome is not the current target. H3 connection survived idle, a new handshake after server closure had no PSK/0-RTT.
Verdict: Above low-level uTLS for ready-made HTTP3 API
- ✕ Chrome154
- ImpersonateChrome differs
- ✕ Safari26 mac/iOS
- Four new TCP connections tested: Safari 16 with target-version UA. No ready-made current profile, stable TLS fields differ from the reference. For uTLS, HTTP/2 remains a separate transport. Results apply to these presets and do not deny the ability to build custom ClientHelloSpec.
- ✕ Firefox156
- Four new TCP connections tested: Firefox 120 with target-version UA. No ready-made current profile, stable TLS fields differ from the reference. For uTLS, HTTP/2 remains a separate transport. Results apply to these presets and do not deny the ability to build custom ClientHelloSpec.
- ✕ Edge154
- Four new TCP connections tested: Chrome 120 with target-version UA. No ready-made current profile, stable TLS fields differ from the reference. For uTLS, HTTP/2 remains a separate transport. Results apply to these presets and do not deny the ability to build custom ClientHelloSpec.
- ✕ Chrome Android154
- Four new TCP connections tested: Chrome 120 with target-version UA. No ready-made current profile, stable TLS fields differ from the reference. For uTLS, HTTP/2 remains a separate transport. Results apply to these presets and do not deny the ability to build custom ClientHelloSpec.
- ✕ HTTP/3
- H3 works, raw fields/SETTINGS differ
- △ HTTP/2
- Configuration possible, target not matched
- ✕ trust_anchors
- Required51764 absent
- △ Client Hints
- Manual headers
- ✕ GREASE / shuffling
- Tested154 not reproduced
- ✕ Resumption
- Four new TCP sessions each for ImpersonateChrome/Safari/Firefox: no pre_shared_key. Chromium/Firefox resumed-fingerprint transitions not reproduced, old Safari profile does not confirm current Safari as a whole.
- △ Integration
- Go API / low-level options
- △ Required profiles
- Assemble the required profile
- △ Keeping profiles current
- Partial database, custom maintained yourself
- △ Code / stack
- Go
12. uTLS + Go HTTP2
Strengths: Maximum ClientHelloSpec freedom
Weaknesses / limitations: TLS alone does not fix HTTP2/3
Verdict: A tool for building a client, not a ready-made target
- ✕ Chrome154
- Ready-made preset is not154
- ✕ Safari26 mac/iOS
- Four new TCP connections tested: Safari 16 with target-version UA. No ready-made current profile, stable TLS fields differ from the reference. For uTLS, HTTP/2 remains a separate transport. Results apply to these presets and do not deny the ability to build custom ClientHelloSpec.
- ✕ Firefox156
- Four new TCP connections tested: Firefox 120 with target-version UA. No ready-made current profile, stable TLS fields differ from the reference. For uTLS, HTTP/2 remains a separate transport. Results apply to these presets and do not deny the ability to build custom ClientHelloSpec.
- ✕ Edge154
- Four new TCP connections tested: Edge 85 with target-version UA. No ready-made current profile, stable TLS fields differ from the reference. For uTLS, HTTP/2 remains a separate transport. Results apply to these presets and do not deny the ability to build custom ClientHelloSpec.
- ✕ Chrome Android154
- Four new TCP connections tested: Chrome 133 with target-version UA. No ready-made current profile, stable TLS fields differ from the reference. For uTLS, HTTP/2 remains a separate transport. Results apply to these presets and do not deny the ability to build custom ClientHelloSpec.
- ✕ HTTP/3
- The tested uTLS + golang.org/x/net/http2 combination has no QUIC/HTTP3 transport. Adding another QUIC library would be a different combination.
- ✕ HTTP/2
- Standard Go H2 differs
- ✓ trust_anchors
- Supported GenericExtension in ClientHelloSpec sent reference trust_anchors51764 payload. Four successful requests, captured extension bytes matched. Manual Spec assembly required, not a ready-made current browser profile. PreferSkipResumptionOnNilExtension=true used for the Spec without PSK.
- △ Client Hints
- Manual
- △ GREASE / shuffling
- Flexible Spec, setup required
- △ Resumption
- Cache/Spec managed by developer
- ✕ Integration
- Low-level integration
- △ Required profiles
- Build TLS and H2 separately
- ✕ Keeping profiles current
- Maintain custom profiles yourself
- △ Code / stack
- Go, custom transport
Details of supported configuration tested
Low-level capabilities are assessed separately: uTLS’s supported GenericExtension in ClientHelloSpec sent reference trust_anchors51764. Four successful requests and raw ClientHello confirmed matching bytes for this extension, earning green in that column. Manual Spec assembly and separate H2 transport remain necessary, matching one extension does not turn ready-made Chrome 133 into current Chrome 154.
13. impit0.14.5
Strengths: Simple API and working H3
Weaknesses / limitations: No exact current Chrome/Safari, H2/QUIC defects. H3 PSK works after idle, but no early HTTP.
Verdict: H3 support did not improve impersonation accuracy
- ✕ Chrome154
- 151: different TLS/H2
- ✕ Safari26 mac/iOS
- No Safari in tested enum
- ✕ Firefox156
- 144 is not156
- ✕ Edge154
- Chrome with Edge UA is not Edge
- ✕ Chrome Android154
- Android: different TLS/H2
- ✕ HTTP/3
- H3 works, raw fields differ greatly
- ✕ HTTP/2
- Different SETTINGS/window
- ✕ trust_anchors
- Required51764 absent
- △ Client Hints
- Headers need alignment
- ✕ GREASE / shuffling
- QUIC retains a TCP-like set
- ✕ Resumption
- Repeat TLS differs
- ✓ Integration
- Fetch Python/Node
- ✕ Required profiles
- 154 rejected
- △ Keeping profiles current
- Project releases
- △ Code / stack
- Python/Node/native
14. CycleTLS2.0.5
Strengths: Supported JA3/JA4R/H2 parameters, working H3, manually matched Firefox H2 summary
Weaknesses / limitations: Chrome 154 JA3 rejects extension 51764, JA4R changes groups, JA3/combined settings select TLS1.2. Safari recognized by brief detection but raw fields differ. Forced H3 uses a separate fresh connection for every GET without PSK/0-RTT.
Verdict: Flexible constructor, but tested settings did not reproduce full current targets, limited Safari/H2 success shown in yellow
- ✕ Chrome154
- JA3/H2 custom154: handshake error
- △ Safari26 mac/iOS
- Supported manual JA4R produced four successful TLS1.3 requests, brief detection recognized Safari 26. Raw supported_groups were29,29,23,24 rather than a set including4588, no exact Safari match. JA3 and combined JA3+JA4R selected TLS1.2.
- ✕ Firefox156
- JA3, JA4R and their combination were tested with reference Firefox 156 HTTP2Fingerprint. JA3/combined mode gave TLS1.2 instead of TLS1.3, JA4R changed groups and failed the handshake. Full156 fingerprint not obtained.
- ✕ Edge154
- JA3/JA4R and combined Edge 154 configuration tested. JA3/combined gave TLS1.2 and different signature_algorithms, JA4R a handshake error. H2 also has extra/different SETTINGS.
- ✕ Chrome Android154
- Tested manual Chrome 154 configuration applied to Android target rejected extension 51764 (trust_anchors). Separate JA4R also failed to handshake. Target not achieved with tested settings.
- ✕ HTTP/3
- H3 works, JA3 did not produce required QUIC
- △ HTTP/2
- Manually supplied Firefox HTTP2Fingerprint matched H2 summary:1:65536,4:131072,5:16384|12517377|0|m,p,a,s. Chrome/Edge/Safari configurations did not fully reproduce SETTINGS, no complete H2 pass across all targets.
- ✕ trust_anchors
- Extension51764 error in H2
- △ Client Hints
- Headers configured manually
- ✕ GREASE / shuffling
- Different QUIC set/groups
- △ Resumption
- Successful Safari JA4R run had four fresh TLS1.3 Hellos without PSK, consistent with no Safari TCP resumption, but raw fields differed. Current Chromium/Firefox PSK transitions not obtained.
- △ Integration
- Node + native / explicit parameters
- △ Required profiles
- JA3/JA4/H2 configuration, not ready-made154
- ✕ Keeping profiles current
- Custom maintenance yourself
- △ Code / stack
- Node/native, integration required
Details of supported configuration tested
CycleTLS2.0.5: three supported configurations tested: JA3 with reference H2 summary, JA4R with signature algorithms, and combined JA3+JA4R. Chrome 154 JA3 rejects extension 51764. JA4R alone sets different groups, Chrome/Edge/Firefox handshakes failed. Safari JA4R produced four successful TLS1.3 requests and brief Safari 26 recognition, but raw groups29,29,23,24 differ from the reference with 4588. JA3 and combined settings chose TLS1.2. Manually supplied Firefox H2 summary matched, so that result counts as partial. Settings are available in the supported CycleTLS API, a particularly clear example of the difference between a brief summary and the full fingerprint.
15. got-scraping4.2.1
Strengths: Convenient header generator
Weaknesses / limitations: EOL and nonbrowser transport
Verdict: Headers do not compensate for TLS/H2
- ✕ Chrome154
- Headers154, different TLS/H2
- ✕ Safari26 mac/iOS
- Four requests with Safari 26.6.2 headers. The generator changes headers and some TLS options, but raw TLS fields and H2 do not match the browser. H2 summary throughout:2:0,4:33554432|4128769|0|m,p,a,s.
- ✕ Firefox156
- Four requests with Firefox 156 headers. The generator changes headers and some TLS options, but raw TLS fields and H2 do not match the browser. H2 summary throughout:2:0,4:33554432|4128769|0|m,p,a,s.
- ✕ Edge154
- Four requests with Edge 154 headers. The generator changes headers and some TLS options, but raw TLS fields and H2 do not match the browser. H2 summary throughout:2:0,4:33554432|4128769|0|m,p,a,s.
- ✕ Chrome Android154
- Four requests with Chrome Android 154 headers. The generator changes headers and some TLS options, but raw TLS fields and H2 do not match the browser. H2 summary throughout:2:0,4:33554432|4128769|0|m,p,a,s.
- ✕ HTTP/3
- got-scraping4.2.1 uses Node TLS and http2-wrapper, no QUIC/HTTP3 API. Auto run used TCP/H2.
- ✕ HTTP/2
- Not Chrome
- ✕ trust_anchors
- Required51764 absent
- ✓ Client Hints
- Headers154 generator
- ✕ GREASE / shuffling
- Transport is not Chrome
- ✕ Resumption
- Repeat TCP measurements showed no pre_shared_key. Changing browser headers did not reproduce Chromium or Firefox fresh/resumed fingerprints. Results apply to the tested got-scraping configuration.
- ✓ Integration
- Got/Node API
- ✕ Required profiles
- No154 transport
- ✕ Keeping profiles current
- EOL, no current maintenance
- △ Code / stack
- Node
16. HTTPX0.28.1
Strengths: Reliable, familiar HTTP API
Weaknesses / limitations: Changing UA does not replace TLS/H2/QUIC
Verdict: Control client, not an impersonation solution
- ✕ Chrome154
- Control: not Chrome
- ✕ Safari26 mac/iOS
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Firefox156
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Edge154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Chrome Android154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ HTTP/3
- HTTP/3 absent from the tested supported transport, no browser QUIC fingerprint obtained.
- △ HTTP/2
- HTTP2 enabled, different
- ✕ trust_anchors
- Captured ClientHello lacks trust_anchors with the reference Chrome 154 ID set.
- △ Client Hints
- Can be set manually
- ✕ GREASE / shuffling
- Captured ClientHello lacks browser GREASE and corresponding Chromium extension shuffling.
- ✕ Resumption
- No Chrome/Firefox PSK transition after reconnecting in the tested configuration. curl used four requests in one process, Python retained its client. This describes that configuration, not the absence of all TLS-session support in the backend.
- ✓ Integration
- Python sync/async
- ✕ Required profiles
- No built-in browser TLS/H2/QUIC profile database, a User-Agent header is not a profile.
- ✕ Keeping profiles current
- No browser-fingerprint database. Updating the HTTP client does not provide one.
- △ Code / stack
- Python
17. Go net/http
Strengths: Reliable, familiar HTTP API
Weaknesses / limitations: Changing UA does not replace TLS/H2/QUIC
Verdict: Control client, not an impersonation solution
- ✕ Chrome154
- Control: not Chrome
- ✕ Safari26 mac/iOS
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Firefox156
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Edge154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Chrome Android154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ HTTP/3
- HTTP/3 absent from the tested supported transport, no browser QUIC fingerprint obtained.
- △ HTTP/2
- HTTP2 supported, different
- ✕ trust_anchors
- Captured ClientHello lacks trust_anchors with the reference Chrome 154 ID set.
- △ Client Hints
- Can be set manually
- ✕ GREASE / shuffling
- Captured ClientHello lacks browser GREASE and corresponding Chromium extension shuffling.
- △ Resumption
- Server-accepted TLS1.3 PSK resumption confirmed. Repeats retain native Go/Node fingerprints, without emulating target-browser transitions.
- ✓ Integration
- Standard Go API
- ✕ Required profiles
- No built-in browser TLS/H2/QUIC profile database, a User-Agent header is not a profile.
- ✕ Keeping profiles current
- No browser-fingerprint database. Updating the HTTP client does not provide one.
- △ Code / stack
- Go
Details of supported configuration tested
Repeat TLS: Go net/http with ClientSessionCache enabled and built-in Node fetch obtained server-accepted PSK but retained nonbrowser ClientHello. This is partial transport capability, not confirmed resumed-browser emulation. HTTPX, requests, Windows curl and ready-made req/v3 profiles did not produce required Chromium/Firefox PSK transitions in tested settings. The assessment describes reproducible results for those configurations, not an inherent inability of their TLS backend to store sessions. For Safari, no TCP PSK is compared against Safari references and is not penalized by itself.
18. Windows curl / Schannel
Strengths: Reliable, familiar HTTP API
Weaknesses / limitations: Changing UA does not replace TLS/H2/QUIC
Verdict: Control client, not an impersonation solution
- ✕ Chrome154
- Control: not Chrome
- ✕ Safari26 mac/iOS
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Firefox156
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Edge154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Chrome Android154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ HTTP/3
- HTTP/3 absent from the tested supported transport, no browser QUIC fingerprint obtained.
- ✕ HTTP/2
- H2 depends on build, measured transport differs
- ✕ trust_anchors
- Captured ClientHello lacks trust_anchors with the reference Chrome 154 ID set.
- △ Client Hints
- Can be set manually
- ✕ GREASE / shuffling
- Captured ClientHello lacks browser GREASE and corresponding Chromium extension shuffling.
- ✕ Resumption
- No Chrome/Firefox PSK transition after reconnecting in the tested configuration. curl used four requests in one process, Python retained its client. This describes that configuration, not the absence of all TLS-session support in the backend.
- ✓ Integration
- CLI/proxy
- ✕ Required profiles
- No built-in browser TLS/H2/QUIC profile database, a User-Agent header is not a profile.
- ✕ Keeping profiles current
- No browser-fingerprint database. Updating the HTTP client does not provide one.
- △ Code / stack
- CLI across stacks
19. requests2.32.5
Strengths: Reliable, familiar HTTP API
Weaknesses / limitations: Changing UA does not replace TLS/H2/QUIC
Verdict: Control client, not an impersonation solution
- ✕ Chrome154
- Control: not Chrome
- ✕ Safari26 mac/iOS
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Firefox156
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Edge154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Chrome Android154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ HTTP/3
- HTTP/3 absent from the tested supported transport, no browser QUIC fingerprint obtained.
- ✕ HTTP/2
- H1, no H2 in this transport
- ✕ trust_anchors
- Captured ClientHello lacks trust_anchors with the reference Chrome 154 ID set.
- △ Client Hints
- Can be set manually
- ✕ GREASE / shuffling
- Captured ClientHello lacks browser GREASE and corresponding Chromium extension shuffling.
- ✕ Resumption
- No Chrome/Firefox PSK transition after reconnecting in the tested configuration. curl used four requests in one process, Python retained its client. This describes that configuration, not the absence of all TLS-session support in the backend.
- ✓ Integration
- Python sync
- ✕ Required profiles
- No built-in browser TLS/H2/QUIC profile database, a User-Agent header is not a profile.
- ✕ Keeping profiles current
- No browser-fingerprint database. Updating the HTTP client does not provide one.
- △ Code / stack
- Python
20. Node fetch
Strengths: Reliable, familiar HTTP API
Weaknesses / limitations: Changing UA does not replace TLS/H2/QUIC
Verdict: Control client, not an impersonation solution
- ✕ Chrome154
- Control: not Chrome
- ✕ Safari26 mac/iOS
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Firefox156
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Edge154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ Chrome Android154
- No browser profiles. Requests with this target’s UA retained native TLS fingerprints, changing UA does not reproduce a browser.
- ✕ HTTP/3
- HTTP/3 absent from the tested supported transport, no browser QUIC fingerprint obtained.
- ✕ HTTP/2
- Measured H1
- ✕ trust_anchors
- Captured ClientHello lacks trust_anchors with the reference Chrome 154 ID set.
- △ Client Hints
- Can be set manually
- ✕ GREASE / shuffling
- Captured ClientHello lacks browser GREASE and corresponding Chromium extension shuffling.
- △ Resumption
- Server-accepted TLS1.3 PSK resumption confirmed. Repeats retain native Go/Node fingerprints, without emulating target-browser transitions.
- ✓ Integration
- Built-in fetch
- ✕ Required profiles
- No built-in browser TLS/H2/QUIC profile database, a User-Agent header is not a profile.
- ✕ Keeping profiles current
- No browser-fingerprint database. Updating the HTTP client does not provide one.
- △ Code / stack
- Node
A browser fingerprint combines TLS, HTTP2/3, headers and next-connection behavior. Choose a solution by the tested target and maintenance cost: a browser name in a preset and a changed User-Agent do not replace these measurements.
The entire browser-profile library is available on BlankTrail Proxy’s free Lite plan. You can download the application and test the profiles you need yourself.

