Noureddine RAMDI / surf: a Go HTTP client for authentic browser impersonation with TLS and QUIC fingerprinting

Created Sat, 23 May 2026 20:41:14 +0000 Modified Sat, 23 May 2026 20:41:27 +0000

enetx/surf

Browser impersonation often stops at faking headers or user agents, but SURF takes it a step further by replicating browser fingerprints at every protocol layer. This means it doesn’t just pretend to be Chrome or Firefox in HTTP headers; it mimics TLS handshakes, HTTP/2 settings frames, and even QUIC transport parameters typical of those browsers. For anyone dealing with anti-bot defenses or needing to blend in with real browser traffic, SURF offers a level of detail that’s rare in HTTP clients.

What surf does and how it works under the hood

SURF is an HTTP client library written in Go designed specifically for browser impersonation and anti-detection. It’s not just about setting user-agent strings; SURF replicates the fingerprints of Chrome (version 145) and Firefox (version 148) at multiple layers of the network stack.

Under the hood, it reproduces JA3 and JA4 TLS client hello fingerprints, which capture the TLS handshake characteristics. This is important because many anti-bot systems fingerprint clients based on subtle variations in TLS handshakes.

Beyond TLS, SURF replicates HTTP/2 SETTINGS frames, which define connection parameters negotiated by browsers, and it also supports HTTP/3 over QUIC, mimicking the transport parameters and behavior of real browsers. It even goes as far as replicating WebKit-style multipart form boundary strings.

Architecturally, SURF exposes a fluent builder API that lets you configure requests in a natural way. Once configured, it converts internally to a standard net/http.Client instance so you can plug it directly into any existing Go code or third-party libraries expecting an HTTP client.

The codebase requires Go 1.25 or newer, reflecting its use of the latest Go features. It supports advanced features like HTTP/3 over SOCKS5 UDP proxies, DNS-over-TLS for encrypted DNS queries, automatic fallback between HTTP versions, connection pooling, and retry mechanisms. Error handling follows a result-type pattern, making it explicit and clear.

Technical strengths and design tradeoffs

The standout technical strength of SURF is the depth of its fingerprint replication. Many HTTP clients or scraping tools fake user-agent headers or a handful of TLS parameters, but SURF targets the entire stack — TLS JA3/JA4 fingerprints, HTTP/2 frame settings, QUIC parameters — all of which are critical for bypassing sophisticated anti-bot defenses.

This fidelity comes with complexity. Accurately replicating browser fingerprints at the protocol layer requires deep understanding of browser internals and TLS/QUIC protocols. The code to reproduce these fingerprints is necessarily detailed and specialized.

The tradeoff is that SURF is more heavyweight and opinionated than a basic HTTP client. It’s designed for scenarios where blending in with real browsers is essential, not for general-purpose HTTP calls. If you don’t care about fingerprinting beyond headers, SURF might be overkill.

The design choice to expose a fluent builder API that outputs a standard net/http.Client is a strong point. It keeps the DX smooth and lets developers integrate SURF into existing ecosystems without rewriting their HTTP handling code.

Supporting HTTP/3 over SOCKS5 UDP with DNS-over-TLS shows a focus on privacy and modern transport protocols — but these features also increase the codebase complexity and require newer Go versions.

Overall, the code quality appears solid with clear error handling and modern Go idioms. The project’s specialized focus means it’s less of a drop-in replacement for a general HTTP client and more of a targeted tool for anti-detection use cases.

Quick start

Installation is straightforward with a single Go get command:

go get -u github.com/enetx/surf

This requires Go 1.25 or later.

To make a basic GET request, you can use:

package main

import (
    "fmt"
    "log"
    "github.com/enetx/surf"
)

func main() {
    resp := surf.NewClient().Get("https://api.github.com/users/github").Do()
    if resp.IsErr() {
        log.Fatal(resp.Err())
    }

    fmt.Println(resp.Ok().Body.String().Unwrap())
}

The response handling uses a result-type pattern where you check if the response is an error before accessing the successful result.

For JSON response handling, you can unmarshal directly into a struct:

type User struct {
    Name     string `json:"name"`
    Company  string `json:"company"`
    Location string `json:"location"`
}

resp := surf.NewClient().Get("https://api.github.com/users/github").Do()
if resp.IsOk() {
    var user User
    resp.Ok().Body.JSON(&user)
    fmt.Printf("User: %+v\n", user)
}

This makes it easy to work with API responses in idiomatic Go.

Verdict

SURF is a highly specialized Go HTTP client tailored for scenarios where authentic browser impersonation is critical. Its replication of TLS JA3/JA4 fingerprints, HTTP/2 settings, and QUIC parameters sets it apart from typical HTTP clients that only fake headers.

If you’re building scrapers, bots, or tools that need to bypass sophisticated anti-bot defenses relying on fingerprinting at the network protocol level, SURF is worth exploring. The fluent builder API and compatibility with the standard library client make integration relatively smooth.

The tradeoff is complexity and a narrower focus. It’s not a general-purpose HTTP client replacement but a precision tool. Also, the requirement for Go 1.25+ and advanced protocols like HTTP/3 over SOCKS5 UDP means it’s best suited for modern Go environments.

In production, expect to spend some time tuning fingerprints and handling edge cases specific to your target sites. But for anyone needing realistic browser-level TLS and QUIC fingerprinting in Go, SURF hits a niche that most libraries don’t address.


→ GitHub Repo: enetx/surf ⭐ 1,715 · Go