返回资讯中心
外部精选
软件工程
#开发者实时流

Impersonate-HTTP – byte-exact browser TLS fingerprints for Go's http.Client

A Go http.Client whose TLS handshake and HTTP/2 frames are byte-identical to a

Hacker News 最新NorthWeb2026/10/25 · 11 天前15 分钟阅读

以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。

A Go http.Client whose TLS handshake and HTTP/2 frames are byte-identical to a real browser's, so requests survive JA3 / JA4 / Akamai HTTP/2 fingerprinting (Cloudflare, Akamai, DataDome, PerimeterX, …).

Go-native: no headless browser, no CGo, no libcurl, no subprocess — just a stock *http.Client built on uTLS.

go get github.com/North-web-dev/impersonate-http

package main

import ( "fmt" "io"

impersonate "github.com/North-web-dev/impersonate-http" )

func main() { client := impersonate.New(impersonate.Chrome) // a stock *http.Client resp, err := client.Get("https://example.com") if err != nil { panic(err) } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(resp.Status, len(body), "bytes") }

Use the returned client exactly as you would http.DefaultClient.

Chrome, ChromeAndroid, Firefox, Safari, Edge, IOS — each sets the matching uTLS ClientHello, the browser's HTTP/2 SETTINGS/WINDOW_UPDATE/pseudo-header fingerprint, and the browser's default headers (User-Agent, sec-ch-ua, Accept, Sec-Fetch-*, …). Look one up by name:

client, ok := impersonate.NewByName("firefox")

Or plug the transport into an existing client:

c := &http.Client{Transport: impersonate.NewTransport(impersonate.Safari)}

Caller-set headers are never overwritten by a profile's defaults.

Route through a socks5:// or http(s):// proxy (with optional credentials):

client := impersonate.New(impersonate.Chrome, impersonate.WithProxy("http://user:pass@host:8080"), impersonate.WithTimeout(20*time.Second), )

WithDialer accepts any custom dialer if you need full control; ProxyDialer exposes the proxy dial function on its own.

Captured with go run ./cmd/measure against tls.peet.ws/api/all on 2026-10-25, uTLS v1.8.2. These are real, reproducible measurements — run cmd/measure yourself to regenerate them.

JA4 is the fingerprint to match on, not JA3. Chrome shuffles its TLS extension order on every connection (Chromium ≥ 110), so its JA3 hash is non-deterministic — exactly like a real Chrome. JA4 sorts extensions, so it is stable; the cipher core (8daaf6152771 Chrome/Edge, 5b57614c22b0 Firefox, a09f3c656075 Safari) matches the published desktop-browser values, and the HTTP/2 Akamai fingerprint is byte-exact per profile.

  • Edge caveat: real Edge 131 permutes extensions like Chrome, but uTLS's HelloEdge_Auto preset does not, so the Edge ClientHello is close (correct TLS version, cipher list, ALPN) but does not reproduce Chromium's per-connection shuffle. Prefer Chrome if you need the shuffle behaviour.

Profiles track uTLS's *_Auto templates, so they follow the current stable browser as uTLS updates; the exact JA4 third segment can shift by a browser point release.

vs curl_cffi: curl_cffi is Python bound to a patched libcurl (CGo / native BoringSSL). impersonate-http is pure Go — no CGo, no libcurl, compiles to a single static binary, and gives you a real *http.Client you drop into existing Go code and middleware.

vs utls directly: uTLS gives you the ClientHello but stops at the TLS layer — you still have to wire up ALPN, the HTTP/2 framing fingerprint, connection pooling, headers and proxies yourself. This library is that wiring, packaged as a drop-in http.Client.

Neither this library nor curl_cffi runs a headless browser.

The same TLS fingerprint works for wss:// — plug the profile's dialer into any WebSocket client:

// coder/websocket websocket.Dial(ctx, "wss://host/path", &websocket.DialOptions{ HTTPClient: &http.Client{Transport: impersonate.NewTransport(impersonate.Chrome)}, })

// gorilla/websocket d := websocket.Dialer{NetDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return impersonate.Chrome.DialTLSContext(ctx, network, addr) }}

HTTP API

cmd/serve wraps the library in a language-agnostic fetch API — POST a URL and a profile, get back the response fetched with that browser's exact fingerprint. Handy when the rest of your stack isn't Go.

go run ./cmd/serve -addr :8080

or: docker build -t impersonate . && docker run -p 8080:8080 impersonate

curl -s localhost:8080/fetch -d '{ "url": "https://example.com", "profile": "chrome", "proxy": "http://user:pass@host:8080" }'

-> {"status":200,"final_url":"...","headers":{...},"body":"<!doctype html>...","elapsed_ms":812}

Endpoint Purpose

POST /fetch Fetch url with a profile. Fields: profile, method, headers, body, proxy, timeout_ms, follow_redirects. br/zstd/gzip bodies are decoded for you.

GET /profiles List available profiles.

GET /usage Caller's rate/quota usage (does not count against it).

GET /healthz Liveness.

Auth & quotas are optional. Pass a single key with -key, or a plans file with -keys keys.json for per-key rate limits and daily quotas:

[{"key": "acme-key", "name": "acme", "rps": 5, "daily": 10000}]

Present the key as Authorization: Bearer <key> or X-API-Key: <key>. Without either flag the API is open.

Each connection is dialed with uTLS: a raw TCP dial, then a UClient handshake using the profile's ClientHelloID. ALPN is probed once per host to pick HTTP/2 or HTTP/1.1; the chosen transport re-runs the uTLS handshake for every pooled connection, so keep-alive and connection reuse work as usual. For HTTP/2, a hand-rolled client over http2.Framer emits the browser's exact SETTINGS, connection WINDOW_UPDATE, and pseudo-header order.

HTTP/3 (QUIC) ClientHello impersonation.

Edge extension-permutation parity with current Chromium.

Profile auto-update sourced from fingerprint-db.

More browser variants (Firefox ESR, Opera/Brave).

Reproduce the measurements

cmd/measure — in this repo; captures the JA3/JA4/Akamai this library emits per profile against tls.peet.ws.

fingerprint-db — the measured JA3/JA4/Akamai fingerprints these profiles reproduce (JSON + loaders).

fpcheck — self-hosted rig to read your own JA3/JA4/JA4H + HTTP/2 fingerprint.

Support

If this project saves you time, you can support development:

Provided as is, without warranty of any kind, for lawful automation, testing, and research. You are solely responsible for complying with the terms and laws that apply to the sites you access. The authors accept no liability for misuse.

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →