Map

surf

Toolbox toolboxgohttp-clienttls-fingerprintingbrowser-impersonationscrapingquicGoMIT โ†ณ show in map Markdown
title
surf
type
toolbox
summary
Go HTTP client impersonating Chrome and Firefox down to the TLS handshake and QUIC packets
tags
go, http-client, tls-fingerprinting, browser-impersonation, scraping, quic
language
Go
license
MIT
created
2026-05-06
updated
2026-05-06

Go HTTP client library that impersonates real browsers down to the TLS handshake and the QUIC initial packet. Targets the same niche as curl-impersonate and tls-client: web automation and scraping against sites that fingerprint clients (Cloudflare, Akamai, PerimeterX, DataDome). Mimics Chrome v145 and Firefox v148 across Windows, macOS, Linux, Android, and iOS, with HTTP/2 SETTINGS frames, header ordering, and full HTTP/3 over QUIC.

What it does

Standard net/http.Client is fingerprintable: its TLS ClientHello, HTTP/2 SETTINGS, and header order are constant and don't match any real browser. Anti-bot vendors compute JA3/JA4 hashes from the TLS handshake and HTTP/2 fingerprints from frame contents, and Go traffic stands out. SURF replaces the transport with one that produces byte-equivalent handshakes to a chosen browser version + OS combo, including the QUIC variant for HTTP/3-only sites.

The library also wraps the request lifecycle in a fluent builder API and a Result-type-based error model (g.Result[T]) borrowed from enetx/g, so it doesn't feel like stdlib net/http.

How it works

TLS fingerprinting rides on uTLS (refraction-networking/utls), the standard Go fork that exposes ClientHello specs. SURF picks HelloID or full HelloSpec per browser/OS combo. JA3/JA4 fingerprints can also be set explicitly or randomized.

QUIC/HTTP/3 uses quic-go with browser-matched parameters: header ordering, frame ordering, SETTINGS values, and a JA4QUIC fingerprint built from the Initial Packet plus TLS ClientHello. SOCKS5 UDP proxies are supported so HTTP/3 stays available behind a proxy; HTTP proxies trigger automatic fallback to HTTP/2.

HTTP/2 is configurable at the SETTINGS-frame level: header table size, push enable, initial window size, max concurrent streams, etc. โ€” set per browser preset to match the real values Chrome and Firefox send.

Header ordering is preserved end-to-end. Most Go HTTP libraries normalize header order via the standard library's http.Header map; SURF uses the enetx/http fork that keeps order, because anti-bot systems treat header sequence as a fingerprint.

Builder API chains configuration: surf.NewClient().Builder().Impersonate().Chrome().JA().Randomized().HTTP2Settings()...Build(). Middleware plugs in via .With(fn, priority...) for request, response, or client-scope hooks.

Std() escape hatch converts a configured SURF client back into *net/http.Client for use with libraries that want stdlib types. Most fingerprinting features survive the conversion; retry logic, response caching, remote-address tracking, and timing don't.

Foundation stack: enetx/http (forked stdlib HTTP), quic-go, uTLS, enetx/g (Result/Option types and string utilities).

Usage

Basic Chrome impersonation:

client := surf.NewClient().
    Builder().
    Impersonate().Chrome().
    Build().Unwrap()

resp := client.Get("https://example.com").Do()
if resp.IsOk() {
    fmt.Println(resp.Ok().Body.String().Unwrap())
}

iOS Chrome with HTTP/3 forced and a SOCKS5 UDP proxy:

client := surf.NewClient().
    Builder().
    Impersonate().IOS().Chrome().
    Proxy("socks5://127.0.0.1:1080").
    ForceHTTP3().
    Build().Unwrap()

Session with cookie jar, retry, and body cache:

client := surf.NewClient().
    Builder().
    Impersonate().Firefox().
    Session().
    Retry(3, 2*time.Second).
    CacheBody().
    Build().Unwrap()

Multipart upload with mixed sources:

mp := surf.NewMultipart().
    Field("description", "Files").
    File("document", g.NewFile("/path/doc.pdf")).
    FileBytes("data", "data.json", g.Bytes(`{"k":"v"}`))

resp := client.Post("https://api.example.com/upload").Multipart(mp).Do()

Limitations

  • Requires Go 1.25+.
  • Chrome v145 and Firefox v148 are the impersonation targets; staying current with new browser releases is on the maintainer.
  • Result-type API (g.Result[T] from enetx/g) is not idiomatic Go and pulls a non-trivial generic-utilities dependency. Existing codebases need adapter shims.
  • Single-author project (Enetx). Active but bus-factor-1.
  • The Std() escape hatch loses retry, body caching, remote address, and timing info.
  • TLS fingerprinting via uTLS is a moving target โ€” anti-bot vendors update detection regularly, and a working impersonation today doesn't guarantee one next quarter.

Cross-references

Lives in the same anti-fingerprinting space as the proxy core in sing-box, the VLESS-cluster tutorials in xray-tutorial, and the broader russia-vpn-bypass-state-2026-04 toolkit โ€” though those operate at the transport-protocol layer while SURF operates at the application HTTP layer. Useful for agent harnesses building scrapers; see sandboxing-ai-agents for the orchestration side.

Repo

github.com/enetx/surf โ€” Go, MIT, 1.7k stars (88 forks). Single-author, actively maintained.