
Surf is a powerful, feature-rich HTTP client library for Go that makes working with HTTP requests intuitive and enjoyable. With advanced features like browser impersonation, JA3/JA4 fingerprinting, and comprehensive middleware support, Surf provides everything you need for modern web interactions.
HelloID and HelloSpecnet/http.Client for third-party library integrationgo get -u github.com/enetx/surf
Required Go version: 1.25+
Surf provides seamless integration with Go's standard net/http package, allowing you to use Surf's advanced features with any library that expects a standard *http.Client.
// Create a Surf client with advanced features
surfClient := surf.NewClient().
Builder().
Impersonate().Chrome().
Session().
Build().
Unwrap()
// Convert to standard net/http.Client
stdClient := surfClient.Std()
// Use with any third-party library
// Example: AWS SDK, Google APIs, OpenAI client, etc.
resp, err := stdClient.Get("https://api.example.com")
Preserved Features When Using Std(): - ✅ JA3/TLS fingerprinting - ✅ HTTP/2, HTTP/3 settings && fingerprinting - ✅ Browser impersonation headers - ✅ Ordered headers - ✅ Cookies and sessions - ✅ Proxy configuration - ✅ Custom headers and User-Agent - ✅ Timeout settings - ✅ Redirect policies - ✅ Request/Response middleware
Limitations with Std(): - ❌ Retry logic (implement at application level) - ❌ Response body caching - ❌ Remote address tracking - ❌ Request timing information
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())
}
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)
}
client := surf.NewClient().
Builder().
Impersonate().
Chrome(). // Latest Chrome v145
Build().
Unwrap()
resp := client.Get("https://example.com").Do()
client := surf.NewClient().
Builder().
Impersonate().
RandomOS(). // Randomly selects Windows, macOS, Linux, Android, or iOS
Firefox(). // Latest Firefox v148
Build().
Unwrap()
// iOS Chrome
client := surf.NewClient().
Builder().
Impersonate().
IOS().
Chrome().
Build().
Unwrap()
// Android Chrome
client := surf.NewClient().
Builder().
Impersonate().
Android().
Chrome().
Build().
Unwrap()
// Automatic HTTP/3 with Chrome fingerprinting
client := surf.NewClient().
Builder().
Impersonate().Chrome().
ForceHTTP3(). // Auto-detects Chrome and applies appropriate HTTP/3 settings
Build().
Unwrap()
resp := client.Get("https://cloudflare-quic.com/").Do()
if resp.IsOk() {
fmt.Printf("Protocol: %s\n", resp.Ok().Proto) // HTTP/3.0
}
// Firefox with HTTP/3 fingerprinting
client := surf.NewClient().
Builder().
Impersonate().Firefox().
ForceHTTP3(). // Auto-detects Firefox and applies Firefox HTTP/3 settings
Build().
Unwrap()
resp := client.Get("https://cloudflare-quic.com/").Do()
// Custom fingerprint settings
client := surf.NewClient().
Builder().
HTTP3Settings().Grease().Set().
Build().
Unwrap()
HTTP/3 automatically handles compatibility issues:
// With HTTP proxy - automatically falls back to HTTP/2
client := surf.NewClient().
Builder().
Proxy("http://proxy:8080"). // HTTP proxies incompatible with HTTP/3
ForceHTTP3(). // Will use HTTP/2 instead
Build().
Unwrap()
// With SOCKS5 proxy - HTTP/3 works over UDP
client := surf.NewClient().
Builder().
Proxy("socks5://127.0.0.1:1080"). // SOCKS5 UDP proxy supports HTTP/3
ForceHTTP3(). // Will use HTTP/3 over SOCKS5
Build().
Unwrap()
// With DNS settings - works seamlessly
client := surf.NewClient().
Builder().
DNS("8.8.8.8:53"). // Custom DNS works with HTTP/3
ForceHTTP3().
Build().
Unwrap()
// With DNS-over-TLS - works seamlessly
client := surf.NewClient().
Builder().
DNSOverTLS().Google(). // DoT works with HTTP/3
ForceHTTP3()
Build().
Unwrap()
Key HTTP/3 Features:
- ✅ Complete QUIC Fingerprinting: Full Chrome and Firefox QUIC transport parameter matching
- ✅ Header Ordering: Perfect browser-like header sequence preservation
- ✅ SOCKS5 UDP Support: HTTP/3 works seamlessly over SOCKS5 UDP proxies
- ✅ Automatic Fallback: Smart fallback to HTTP/2 when HTTP proxies are configured
- ✅ DNS Integration: Custom DNS and DNS-over-TLS support
- ✅ JA4QUIC Support: Advanced QUIC fingerprinting with Initial Packet + TLS ClientHello
- ✅ Order Independence: ForceHTTP3() works regardless of call order
// Use specific browser versions
client := surf.NewClient().
Builder().
JA().
Chrome(). // Latest Chrome
Build().
Unwrap()
// Randomized fingerprints for evasion
client := surf.NewClient().
Builder().
JA().
Randomized(). // Random TLS fingerprint
Build().
Unwrap()
// With custom HelloID
client := surf.NewClient().
Builder().
JA().
SetHelloID(utls.HelloChrome_Auto).
Build().
Unwrap()
// With custom HelloSpec
client := surf.NewClient().
Builder().
JA().
SetHelloSpec(customSpec).
Build().
Unwrap()
client := surf.NewClient().
Builder().
HTTP2Settings().
HeaderTableSize(65536).
EnablePush(0).
InitialWindowSize(6291456).
MaxHeaderListSize(262144).
ConnectionFlow(15663105).
Set().
Build().
Unwrap()
client := surf.NewClient().
Builder().
HTTP3Settings().
QpackMaxTableCapacity(65536).
MaxFieldSectionSize(262144).
QpackBlockedStreams(100).
H3Datagram(1).
Grease().
Set().
Build().
Unwrap()
// Single proxy
client := surf.NewClient().
Builder().
Proxy("http://proxy.example.com:8080").
Build().
Unwrap()
Surf supports HTTP/3 over SOCKS5 UDP proxies, combining the benefits of modern QUIC protocol with proxy functionality:
// HTTP/3 over SOCKS5 UDP proxy
client := surf.NewClient().
Builder().
Proxy("socks5://127.0.0.1:1080").
Impersonate().Chrome().
ForceHTTP3(). // Uses HTTP/3 over SOCKS5 UDP
Build().
Unwrap()
// SOCKS5 with custom DNS resolution
client := surf.NewClient().
Builder().
DNS("8.8.8.8:53"). // Custom DNS resolver
Proxy("socks5://proxy:1080"). // SOCKS5 UDP proxy
ForceHTTP3(). // HTTP/3 over SOCKS5
Build().
Unwrap()
client := surf.NewClient().
Builder().
With(func(req *surf.Request) error {
req.AddHeaders("X-Custom-Header", "value")
fmt.Printf("Request to: %s\n", req.GetRequest().URL)
return nil
}).
Build().
Unwrap()
client := surf.NewClient().
Builder().
With(func(resp *surf.Response) error {
fmt.Printf("Response status: %d\n", resp.StatusCode)
fmt.Printf("Response time: %v\n", resp.Time)
return nil
}).
Build().
Unwrap()
client := surf.NewClient().
Builder().
With(func(client *surf.Client) error {
// Modify client configuration
client.GetClient().Timeout = 30 * time.Second
return nil
}).
Build().
Unwrap()
user := map[string]string{
"name": "John Doe",
"email": "john@example.com",
}
resp := surf.NewClient().
Post("https://api.example.com/users").
Body(user).
Do()
// Standard form data (field order not guaranteed)
formData := map[string]string{
"username": "john",
"password": "secret",
}
resp := surf.NewClient().
Post("https://example.com/login").
Body(formData).
Do()
// Ordered form data (preserves field insertion order)
orderedForm := g.NewMapOrd[string, string]()
orderedForm.Insert("username", "john")
orderedForm.Insert("password", "secret")
orderedForm.Insert("remember_me", "true")
resp := surf.NewClient().
Post("https://example.com/login").
Body(orderedForm).
Do()
// Single file upload
mp := surf.NewMultipart().
File("file", g.NewFile("/path/to/file.pdf"))
resp := surf.NewClient().
Post("https://api.example.com/upload").
Multipart(mp).
Do()
// With additional form fields
mp := surf.NewMultipart().
Field("description", "Important document").
Field("category", "reports").
File("file", g.NewFile("/path/to/file.pdf"))
resp := surf.NewClient().
Post("https://api.example.com/upload").
Multipart(mp).
Do()
// Simple multipart form with fields only
mp := surf.NewMultipart().
Field("field1", "value1").
Field("field2", "value2")
resp := surf.NewClient().
Post("https://api.example.com/form").
Multipart(mp).
Do()
// Advanced multipart with files from different sources
mp := surf.NewMultipart().
Field("description", "Multiple files").
File("document", g.NewFile("/path/to/doc.pdf")). // Physical file
FileBytes("data", "data.json", g.Bytes(`{"key": "value"}`)). // Bytes with custom filename
FileString("text", "note.txt", "Hello, World!"). // String content
FileReader("stream", "upload.bin", someReader). // io.Reader
ContentType("application/pdf") // Custom Content-Type for last file
resp := surf.NewClient().
Post("https://api.example.com/upload").
Multipart(mp).
Do()
```go client := surf.NewClient(). Builder(). Session(). // Enable cookie jar Build(). Unwrap()
// Login client.Post("https://example.com/login").Body(credentials).Do()
// Subsequent request