All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Chrome 146 preset — New default preset with updated
sec-ch-uabrand rotation ("Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146") and User-Agent version bump. TLS and HTTP/2 fingerprints are identical to Chrome 145/144/143. All-latestaliases now resolve to Chrome 146. All code examples updated to usechrome-latestto avoid version-specific churn. getCookiesDetailed()/getCookieDetailed()— New methods that return Cookie objects with full metadata (domain, path, expires, maxAge, secure, httpOnly, sameSite). Available in all bindings. The existinggetCookies()/getCookie()methods continue to return the old flat format (name→value dict / string) with a deprecation notice — in a future release they will return the same format as the detailed methods.- Userspace UDP receive buffering — On platforms where the kernel limits UDP socket buffer size (Azure Container Apps: 416 KiB), a dedicated drain goroutine now keeps the kernel buffer permanently drained by buffering packets in userspace (256–4096 slots). Prevents silent packet drops, retransmissions, and connection failures for HTTP/3. Activates automatically when the kernel buffer is below 7 MB; zero overhead on systems with proper buffers.
google_connection_optionsQUIC transport parameter — Chrome sendsgoogle_connection_options(0x3128) with value "B2ON" in QUIC handshakes. This was the last missing Chrome-specific transport parameter identified in a full fingerprint audit against azuretls-client.- HPACK never-indexed representation for sensitive headers —
cookie,authorization, andproxy-authorizationnow use the HPACK "Never Indexed" wire encoding (0x10 prefix) matching Chrome's behavior. Previously used "Without Indexing" (0x00 prefix) which anti-bot systems like Akamai can distinguish. tcp_dfoption in Python and Node.js bindings — The DF (Don't Fragment) bit was missing from the Python and Node.js session constructors. Now all 5 TCP fingerprint fields are exposed in all bindings.- All TCP fingerprint fields in .NET binding — The .NET
Sessionconstructor andSessionConfigclass now exposetcpTtl,tcpMss,tcpWindowSize,tcpWindowScale, andtcpDfparameters.
- Fix Cookie API losing domain/path/expiry metadata — The internal cookie jar stored full metadata correctly, but
getCookies()flattened it to a name→value dict, losing domain/path/expiry and causing last-write-wins collisions when two domains set a cookie with the same name.setCookie()now accepts domain/path/flags for domain-scoped cookies —setCookie("name", "value")still works unchanged.deleteCookie()properly removes cookies (was setting to empty string) and accepts an optional domain parameter.clearCookies()calls the Go core directly (was doing a broken client-side loop). All existing scripts continue to work —getCookies()still returns a flat dict,getCookie()still returns a string. Wire behavior, session serialization, and per-requestcookiesparameter are unchanged. - Fix pool H2 path splitting cookies per RFC 9113 — The pool
http2.Transportwas missingDisableCookieSplit: true, causing cookies to be sent as separate HPACK entries instead of a single entry like real Chrome. Detectable by Akamai's H2 fingerprinter.
- TCP/IP fingerprint spoofing disabled by default — Spoofing (TTL, MSS, WindowSize, WindowScale, DF bit) applied to proxy connections breaks connectivity and is useless — the proxy terminates TCP, so the target never sees spoofed values. All 24 presets now ship with zero TCPFingerprint. Users can opt in via
WithTCPFingerprint()(Go) ortcp_ttl/tcp_mssetc. in bindings. - UDP buffer size warnings permanently suppressed — The
log.Printfwarnings about insufficient kernel UDP buffer sizes are removed.setReceiveBuffer/setSendBufferstill attempt to increase buffers best-effort; failures are silently handled by userspace buffering.
- sardanioss/utls v1.10.2 → v1.10.3
- sardanioss/quic-go v1.2.21 → v1.2.23
- sardanioss/net v1.2.4 → v1.2.5
1.6.1-beta.3 - 2026-03-08
- TCP/IP fingerprint spoofing — Spoof OS-level TCP/IP stack parameters (TTL, MSS, Window Size, Window Scale, DF bit) to match the claimed browser platform. Anti-bot systems check SYN packet characteristics to verify Windows/Linux/macOS claims. Platform-specific presets included: Windows (TTL=128, WS=8), Linux (TTL=64, WS=7), macOS (TTL=64, WS=6). Override via
WithTCPFingerprintin Go ortcp_ttl/tcp_mss/tcp_window_size/tcp_window_scaleoptions in bindings. FetchModeNoCors— Simulate subresource loads (<script>,<link>,<img>) withsec-fetch-mode: no-corsand content-type-appropriate Accept headers. Use withFetchDestfield to setsec-fetch-dest(script, style, image).SetForceProtocol()— Switch HTTP protocol version (H1/H2/H3) at runtime without creating a new client. Useful for mimicking Chrome's H2→H3 alt-svc upgrade pattern.
- Fix duplicate Content-Length in H1 transport — The
writeHeadersInOrder"remaining headers" loop wrote headers not in the preset order but did not mark them in the tracking map. The fallback "ensure Content-Length" block then wrote Content-Length a second time. Duplicate Content-Length is an HTTP/1.1 protocol violation — nginx and other strict servers return 400 Bad Request. This affected all H1 POST/PUT/PATCH requests with a body through all language bindings. - Fix bindings sending Navigate headers to API endpoints — The transport-level
applyPresetHeadersalways applied Navigate mode headers (sec-fetch-mode: navigate,upgrade-insecure-requests: 1) regardless of request type. API calls via Python/Node.js/.NET bindings were flagged by WAFs like Incapsula because browser navigation headers on an API call is a bot signal. Now auto-detects CORS mode from the user's Accept header (application/json,*/*, etc.) and adjusts sec-fetch headers accordingly. - Fix Chrome 145 sending unnecessary MAX_FRAME_SIZE — Chrome omits HTTP/2 SETTINGS_MAX_FRAME_SIZE (setting 5), relying on the RFC default of 16384. Our preset was sending it explicitly, creating a fingerprint mismatch.
- H3 header order unified with H2 — Removed separate
H3HeaderOrderfrom presets. Chrome uses the samerequest_->extra_headersordered vector for both H2 and H3 (confirmed from Chromium source). The previous H3-specific order was based on tls3.peet.ws's randomized output (their Go server uses maps internally, losing QPACK header order). - QPACK Never-Index bit for sensitive headers — Cookie, Authorization, and Proxy-Authorization headers are now encoded with the N=1 (Never-Index) bit in QPACK, matching Chrome's behavior of preventing intermediaries from caching sensitive values in dynamic tables.
- H3 SETTINGS frame delivery — Re-added 5ms delay after opening control/QPACK streams to ensure the SETTINGS frame is parsed by the server before request HEADERS arrive. Without this, SETTINGS and request can be bundled in the same packet.
- Deterministic H3 header ordering — Headers not in the preset order are now sorted alphabetically instead of random Go map iteration order. Canonical key lookup added for case-insensitive header matching in QPACK encoder.
- Chrome QUIC Initial packet structure — Fixed to match Chrome's exact packet layout for fingerprint consistency.
- Chrome DefaultInitialRTT — Set to 100ms matching Chrome's PTO (Probe Timeout) behavior.
- quic-go v1.2.18 → v1.2.21
- qpack v0.6.2 → v0.6.3
1.6.1-beta.2 - 2026-02-23
- Fix query parameters duplicated in URL for .NET async methods (
GetAsync,PostAsync) — params were applied in the method then passed again toRequestAsyncwhich applied them a second time (only affected async path with explicit timeout) - Fix
SetProxy()andSetPreset()losinginsecureSkipVerifysetting — recreated child transports started with defaultfalse, ignoring the parent'sverify: falsesetting - Fix query parameter order not preserved in .NET binding — changed
parameterstype fromDictionary<string, string>toIEnumerable<KeyValuePair<string, string>>across all request methods (source-compatible, users can now pass ordered collections likeList<KeyValuePair<>>for order-sensitive APIs)
1.6.1-beta.1 - 2026-02-22
- Custom JA3 fingerprinting — Override the preset's TLS fingerprint with a custom JA3 string. Supports all 25+ known TLS extensions, GREASE filtering, and automatic defaults for unspecified fields. Available via
WithCustomFingerprintin Go andja3option in all bindings (Python, Node.js, .NET, clib). - Custom Akamai HTTP/2 fingerprinting — Override the preset's HTTP/2 SETTINGS, WINDOW_UPDATE, PRIORITY, and pseudo-header order with an Akamai fingerprint string. Available via
WithCustomFingerprintin Go andakamaioption in all bindings. - Extra fingerprint options — Fine-tune TLS extensions beyond what JA3 captures:
tls_signature_algorithms,tls_alpn,tls_cert_compression,tls_permute_extensions. Available viaextra_fpdict in bindings orCustomFingerprintstruct fields in Go. - JA3 parser (
fingerprint/ja3.go) — Converts JA3 strings to uTLSClientHelloSpecwith extension ID toTLSExtensionmapping for 25+ known extensions, GREASE handling, and Chrome-like defaults for signature algorithms, ALPN, and cert compression. - Akamai parser (
fingerprint/akamai.go) — Converts Akamai HTTP/2 fingerprint strings toHTTP2Settings+ pseudo-header order. - JA3/Akamai unit tests — 29 unit tests covering Chrome/Firefox/Safari fingerprints, malformed input, GREASE filtering, extension type verification, defaults merging, and edge cases.
- E2E fingerprint tests — 4 E2E tests against
tls.peet.wsverifying JA3 match, Akamai match, preset sanity, and cross-session reproducibility.
- TLS-only mode is automatically enabled when a custom JA3 fingerprint is set (preset HTTP headers are skipped)
- Extension 50 (
signature_algorithms_cert) now uses a broader Chrome-like list includingPKCS1WithSHA1for legacy certificate chain verification - Extension 51 (
key_share) now generates a key share only for the first preferred curve, matching real browser behavior (previously generated for all curves, which was a detectable fingerprint signal)
- Fix
DoStreammissingconfigErrcheck — invalid Akamai fingerprint errors were silently ignored for streaming requests - Fix H1 speculative TLS fallback unconditionally setting session cache — could cause handshake failures with custom JA3 specs that lack PSK extension
- Fix
ParseJA3mutating caller's*JA3Extrasstruct when filling in defaults — now makes a shallow copy - Fix
SetProxy()andSetPreset()silently dropping custom fingerprint config — recreated transports with nil config, losingCustomJA3,CustomH2Settings, speculative TLS, key log writer, and other settings - Fix
Fork()dropping custom fingerprint settings — forked sessions now copy the parent's transport config (including custom JA3, H2 settings, pseudo-header order) - Fix clib
extra_fpsilently ignored when neitherja3norakamaiis set —tls_permute_extensionsand other extra options now work standalone
1.6.0 - 2026-02-22
- Chrome 145 presets — Added
chrome-145,chrome-145-windows,chrome-145-linux,chrome-145-macos,chrome-145-ios,chrome-145-androidbrowser presets with updated TLS fingerprints and HTTP/2/H3 settings.
- Default preset updated from
chrome-144tochrome-145 - Total available presets increased from 18 to 24
1.6.0-beta.13 - 2026-02-15
session.Fork(n)— Create N sessions sharing cookies and TLS session caches but with independent connections. Simulates multiple browser tabs from the same browser for parallel scraping. Available in Go, Python, Node.js, and C#.session.Warmup(url)— Simulate a real browser page load by fetching HTML and all subresources (CSS, JS, images, fonts) with realistic headers, priorities, and timing. Populates TLS session tickets, cookies, and cache headers before real work begins. Available in Go, Python, Node.js, and C#.- Speculative TLS — Sends CONNECT + TLS ClientHello together on proxy connections, saving one round-trip (~25% faster proxy handshakes). Disabled by default due to compatibility issues with some proxies; enable with
enable_speculative_tls. switch_protocolon Refresh() — Switch HTTP protocol version (h1/h2/h3) when callingRefresh(), persisting for future refreshes.-latestpreset aliases —chrome-latest,firefox-latest,safari-latestaliases that automatically resolve to the newest preset version.available_presets()returns dict — Now returns a dict with protocol support info ({name: {h1, h2, h3}}) instead of a flat list.- Auto Content-Type for JSON POST — Automatically sets
Content-Type: application/jsonwhen body is a JSON object/dict. - C# CancellationToken support — Native Go context cancellation for C# async methods.
- C# Session finalizer — Prevents Go session leaks when
Dispose()is missed. disable_echtoggle — Disable ECH lookup per-session for faster first requests when ECH is not needed.cache-control: max-age=0after Refresh() — Automatically adds cache-control header to requests afterRefresh(), matching real browser F5 behavior.- Local address binding — Bind outgoing connections to a specific local IP address for IPv6 rotation. Available via
WithLocalAddressin Go andlocal_addressoption in bindings. - TLS key logging — Per-session
key_log_fileoption andSSLKEYLOGFILEenvironment variable support for Wireshark TLS inspection. - Fast-path clib bindings — Zero-copy APIs (
httpcloak_fast_*) for high-throughput transfers via C FFI. - New mobile presets — Added
chrome-144-ios,chrome-144-android,safari-18-iospresets.
- Parallel DNS + ECH resolution in SOCKS5 proxy QUIC dial path and H3 transport dial
- Pre-load x509 system root CAs at init to avoid ~40ms delay on first TLS handshake
- Default preset updated from
chrome-131/chrome-143tochrome-latest - Replace
SOCKS5UDPConnwithudpbarafor H3 proxy transport
- Fix H2 head-of-line blocking: release
connsMuduring TCP+TLS dial so other requests aren't blocked - Fix H2 cleanup killing long-running requests by adding in-flight request counter
- Fix H2 per-address dial timeout using
min(remaining_budget/remaining_addrs, 10s) - Fix H1 POST body never sent when preset header order omits
Content-Length - Fix H1 connection returned to pool before body is fully drained
- Fix H1 deadline cleared while response body still being read
- Fix H3 UDP fallback and narrow 0-RTT early data check
- Fix H3 GREASE ID/value and QPACK capacity drift in
Refresh()/recreateTransport() - Fix H3 local address IP family filtering (IPv6 local address connecting to IPv4-only host)
- Fix H3 0-RTT rejection after
Refresh()by re-adding missing preset configurations - Fix speculative TLS causing 30s body read delay on HTTP/2 connections
- Fix speculative TLS blocklist key mismatch in H1 and H2
- Fix
bufio.Readerdata loss in proxy CONNECT for H1 and H2 - Fix corrupted pool connections, swallowed flush errors, nil-proxy guards
- Fix case-sensitive
Connectionheader, H2 cleanup race, dead MASQUE code - Fix nil-return on UDP failure and stale H2 connection entry
- Fix relative path redirect resolution using
net/urlfor proper base URL joining
- Fix
quic.Transportgoroutine leak in SOCKS5 H3 proxy path - Auto-cleanup proxy QUIC resources when connection dies
- Fix proxy CONNECT deadline to respect context timeout in H1 and H2
- Fix
verify: falsenot disabling TLS certificate validation - Fix
connect_todomain fronting connection pool key sharing - Fix POST payload encoding: use
UnsafeRelaxedJsonEscapingfor all JSON serialization - Fix per-request
X-HTTPCloak-TlsOnlyheader support in LocalProxy - Fix bogus fallback values in clib getter functions returning incorrect defaults
- Fix stale default presets (
chrome-131/chrome-143) across all bindings
- Fix async headers not forwarded in Python
get_async()/post_async()methods - Fix clib build missing
httpcloak_fast.gosource file - Remove non-existent
chrome-131preset from all binding defaults
- Fix resource leaks and race conditions across all HTTP transports (comprehensive audit)
- Fix H3 transport
Close()blocking indefinitely on QUIC graceful drain - 8 timeout bugs fixed where context cancellation/deadline was ignored across all transports
wg.Wait()in goroutines now uses channel+select onctx.Done()time.Sleep()in goroutines replaced withselect { case <-time.After(): case <-ctx.Done(): }http.ReadResponse()on proxy connections now setsconn.SetReadDeadline()- QUIC transport
Close()wrapped incloseWithTimeout()in bothRefresh()andClose()paths
1.5.10 - 2025-12-18
Baseline release. This changelog begins tracking changes from this version forward.