impersonate

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 19 Imported by: 0

README

impersonate-http

Go Reference CI Go Report Card Go version

A Go http.Client whose TLS handshake is byte-identical to a real browser's, so requests survive JA3 / JA4 fingerprinting (Cloudflare, Akamai, DataDome, PerimeterX, …) — no headless browser, no CGo, no curl.

client := impersonate.New(impersonate.Chrome)
resp, _ := client.Get("https://example.com")

That's it — a stock *http.Client. Use it exactly as you would http.DefaultClient.

Install

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

Profiles

Chrome, ChromeAndroid, Firefox, Safari, Edge, IOS — each sets the matching uTLS ClientHello 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.

Proxy

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.

Verified

Fingerprints measured against tls.peet.ws:

Profile JA4 JA3
Chrome t13d1516h2_8daaf6152771_… 1d03c132ce29d0d7936acc72f12dd7a7
Firefox t13d1715h2_5b57614c22b0_… b5001237acdf006056b409cc433726b0
Safari t13d2014h2_a09f3c656075_… 773906b0efdefa24a7f2b8eb6985bf37

HTTP/2 (Akamai), Chrome, measured at tools.scrapfly.io: 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p — byte-identical to Chrome.

The t13d1516h2_8daaf6152771 Chrome core (version + cipher list + ALPN + cipher hash) matches a genuine Chrome handshake. Profiles track uTLS's *_Auto templates, so they follow the current stable browser as uTLS updates.

Scope

  • TLS ClientHello (JA3, JA4) — byte-exact per profile.
  • HTTP/2 fingerprint (Akamai) — byte-exact: SETTINGS order + values, WINDOW_UPDATE increment, and pseudo-header order all match the browser. A custom HTTP/2 client (built on http2.Framer + hpack) emits every frame the way the browser does — not Go's stack.
  • Default headers & values — browser-accurate.
  • ⚠️ Header order — pseudo-headers are exact; regular-header order is best-effort (profile order first, then extras).

WebSocket

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.

  • 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.

Roadmap

  • HTTP/3 (QUIC) ClientHello impersonation.
  • Profile auto-update sourced from fingerprint-db.
  • More browser variants (Firefox ESR, Opera/Brave).

How it works

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.

Disclaimer

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.

License

MIT — see LICENSE.

Documentation

Overview

Package impersonate provides an http.Client whose TLS handshake is byte-identical to a real browser's, so requests survive JA3/JA4 fingerprinting (Cloudflare, Akamai, etc.) without a headless browser.

Index

Constants

This section is empty.

Variables

View Source
var (
	Chrome        = chrome()
	ChromeAndroid = chromeAndroid()
	Firefox       = firefox()
	Safari        = safari()
	Edge          = edge()
	IOS           = ios()
)

Built-in browser profiles (latest stable at release time).

View Source
var Profiles = map[string]Profile{
	"chrome": Chrome, "chrome_android": ChromeAndroid, "firefox": Firefox,
	"safari": Safari, "edge": Edge, "ios": IOS,
}

Profiles maps names to profiles for lookup by string.

Functions

func New

func New(p Profile, opts ...Option) *http.Client

New returns an http.Client that impersonates the given browser profile.

func NewByName

func NewByName(name string, opts ...Option) (*http.Client, bool)

NewByName looks a profile up by name (chrome/firefox/safari/edge/ios).

func NewTransport

func NewTransport(p Profile, opts ...Option) http.RoundTripper

NewTransport returns a RoundTripper with the profile's TLS + headers.

Types

type DialFunc added in v0.4.0

type DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error)

DialFunc establishes the raw TCP connection the browser TLS handshake runs over. Override it (e.g. with WithProxy) to route through a proxy.

func ProxyDialer added in v0.4.0

func ProxyDialer(rawurl string) (DialFunc, error)

ProxyDialer returns a DialFunc that tunnels TCP connections through the proxy at rawurl. Supported schemes: socks5, socks5h, http, https.

type Option added in v0.4.0

type Option func(*config)

Option customizes a Client or Transport.

func WithDialer added in v0.4.0

func WithDialer(dial DialFunc) Option

WithDialer routes the underlying TCP connection through dial.

func WithProxy added in v0.4.0

func WithProxy(rawurl string) Option

WithProxy routes connections through a socks5:// or http(s):// proxy. An unparseable URL surfaces as an error on the first request rather than a panic; use ProxyDialer directly if you want the error up front.

func WithTimeout added in v0.4.0

func WithTimeout(d time.Duration) Option

WithTimeout sets the client timeout. It affects New; NewTransport ignores it.

type Profile

type Profile struct {
	Name          string
	ClientHello   utls.ClientHelloID
	Headers       http.Header
	HeaderOrder   []string
	H2Settings    []http2.Setting
	H2ConnWindow  uint32
	H2PseudoOrder []string
}

func (Profile) Dial added in v0.3.0

func (p Profile) Dial(network, addr string) (net.Conn, error)

Dial is DialTLSContext with a background context.

func (Profile) DialTLSContext added in v0.3.0

func (p Profile) DialTLSContext(ctx context.Context, network, addr string) (net.Conn, error)

DialTLSContext dials addr and performs the profile's browser-exact TLS handshake (JA3/JA4 identical to the browser), returning the established connection. Plug it into a WebSocket client to get browser-fingerprinted wss:// connections:

// 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)
}}

Directories

Path Synopsis
cmd
measure command
serve command
Command serve exposes impersonate-http as an HTTP fetch API: POST a URL and a browser profile, get back the response fetched with that browser's exact TLS/HTTP2 fingerprint.
Command serve exposes impersonate-http as an HTTP fetch API: POST a URL and a browser profile, get back the response fetched with that browser's exact TLS/HTTP2 fingerprint.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL