golang

How to use httpcloak: Browser-Identical TLS Fingerprints

Modern anti-bot systems don't just check your headers or cookies. They analyze the cryptographic fingerprint of your TLS handshake to identify automated requests before you send a single byte of application data.

httpcloak is a Go HTTP client library that produces browser-identical TLS and HTTP/2 fingerprints. It makes your requests indistinguishable from real Chrome, Firefox, or Safari traffic at the network level.

In this guide, you'll learn how to install httpcloak, make basic and advanced requests, handle sessions with cookies, configure proxies, and understand why this library works where standard HTTP clients fail.

What is httpcloak?

httpcloak creates HTTP requests that match real browser fingerprints at the TLS, HTTP/2, and HTTP/3 protocol levels. When Cloudflare, Akamai, or PerimeterX examine your connection, they see Chrome 143—not Go's standard library.

The library supports multiple programming languages through native bindings. Go is the core implementation, with Python, Node.js, and C# bindings available.

Here's what httpcloak handles for you automatically:

  • TLS fingerprint matching (JA3/JA4 hashes identical to Chrome)
  • HTTP/2 SETTINGS frame values that match browser implementations
  • HTTP/3 with QUIC fingerprinting for modern sites
  • Header ordering that follows browser-specific sequences
  • Post-quantum cryptography (X25519MLKEM768) that Chrome 131+ uses

Why Go's Standard Library Gets Blocked

Go's net/http package has a recognizable fingerprint that bot detection systems identify instantly. The differences are significant and measurable.

Consider the TLS handshake. Go's standard library offers 13 cipher suites. Chrome offers 16. Go includes 12 TLS extensions. Chrome includes 18. Go lacks GREASE values entirely. Chrome randomizes them on every connection.

The HTTP/2 differences are even more obvious. Go's INITIAL_WINDOW_SIZE is 65,535 bytes. Chrome uses 6,291,456 bytes—nearly 100 times larger. Bot detection systems check these values in the first frames after connection establishment.

When you spoof a Chrome User-Agent but your TLS fingerprint screams "Go HTTP client," you get blocked. httpcloak solves this by matching Chrome's fingerprint at every protocol layer.

Step 1: Install httpcloak

Installation varies by language. Choose the one that matches your project.

Go Installation

Run the following command in your project directory:

go get github.com/sardanioss/httpcloak

This adds httpcloak to your go.mod file and downloads all dependencies.

Python Installation

Install via pip:

pip install httpcloak

The Python bindings provide a requests-compatible API. If you're familiar with Python's requests library, you'll feel right at home.

Node.js Installation

Install via npm:

npm install httpcloak

Both synchronous and asynchronous methods are available for Node.js.

C# Installation

Add the NuGet package:

dotnet add package HttpCloak

Step 2: Make Your First Request

Let's verify that httpcloak works by hitting Cloudflare's trace endpoint. This endpoint returns connection details including the TLS version, HTTP protocol, and key exchange algorithm.

Go Example

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/sardanioss/httpcloak/client"
)

func main() {
    // Create client with Chrome 143 fingerprint
    c := client.NewClient("chrome-143")
    defer c.Close()

    // Make a GET request
    resp, err := c.Get(context.Background(), "https://www.cloudflare.com/cdn-cgi/trace", nil)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Status: %d\n", resp.StatusCode)
    fmt.Printf("Protocol: %s\n", resp.Protocol)
    fmt.Println(resp.Text())
}

The client.NewClient("chrome-143") function creates an HTTP client configured to match Chrome 143's fingerprint exactly. The defer c.Close() ensures connection cleanup.

The resp.Protocol field tells you which protocol was negotiated. You'll see h2 for HTTP/2 or h3 for HTTP/3.

Python Example

import httpcloak

# Simple GET request
r = httpcloak.get("https://www.cloudflare.com/cdn-cgi/trace")

print(f"Status: {r.status_code}")
print(f"Protocol: {r.protocol}")
print(r.text)

Python's API mirrors the popular requests library. No configuration needed for basic usage—httpcloak defaults to Chrome 143.

Node.js Example

const httpcloak = require("httpcloak");

async function main() {
    const r = await httpcloak.get("https://www.cloudflare.com/cdn-cgi/trace");
    
    console.log(`Status: ${r.statusCode}`);
    console.log(`Protocol: ${r.protocol}`);
    console.log(r.text);
}

main();

The async/await pattern works naturally with httpcloak's promise-based API.

What You Should See

A successful response looks like this:

fl=283f39
h=www.cloudflare.com
ip=xxx.xxx.xxx.xxx
ts=1767716387.683
visit_scheme=https
uag=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...
colo=LAX
http=http/3
tls=TLSv1.3
kex=X25519MLKEM768

The kex=X25519MLKEM768 line confirms post-quantum key exchange. This is Chrome's post-quantum cryptography that Go's standard library doesn't support. Bot detection systems specifically check for this.

Step 3: Make POST Requests with JSON

Most APIs require POST requests with JSON payloads. httpcloak handles this with minimal code.

Go POST Example

c := client.NewClient("chrome-143")
defer c.Close()

body := []byte(`{"username": "demo", "password": "secret123"}`)

resp, err := c.Do(context.Background(), &client.Request{
    Method:  "POST",
    URL:     "https://api.example.com/login",
    Body:    body,
    Headers: map[string]string{
        "Content-Type": "application/json",
    },
})

if err != nil {
    log.Fatal(err)
}

fmt.Println(resp.Text())

The c.Do() method accepts a client.Request struct for full control over the request. You specify the method, URL, body, and headers explicitly.

Python POST Example

import httpcloak

r = httpcloak.post("https://api.example.com/login", json={
    "username": "demo",
    "password": "secret123"
})

print(r.status_code)
print(r.json())

The json= parameter automatically serializes your dictionary and sets the Content-Type header. This mirrors how Python's requests library works.

Node.js POST Example

const httpcloak = require("httpcloak");

async function login() {
    const r = await httpcloak.post("https://api.example.com/login", {
        json: { username: "demo", password: "secret123" }
    });
    
    console.log(r.statusCode);
    console.log(r.json());
}

login();

Step 4: Use Sessions to Persist Cookies

When scraping authenticated content, you need cookies to persist between requests. httpcloak sessions handle this automatically.

Go Session Example

// Create a session that persists cookies
session := client.NewSession("chrome-143")
defer session.Close()

ctx := context.Background()

// Login - cookies are saved automatically
session.Post(ctx, "https://example.com/login",
    []byte(`{"user":"myuser","pass":"mypass"}`),
    map[string]string{"Content-Type": "application/json"})

// Subsequent requests include the session cookies
resp, _ := session.Get(ctx, "https://example.com/dashboard", nil)
fmt.Println(resp.Text())

The session stores cookies received in Set-Cookie headers and sends them with subsequent requests. You don't need to manage cookie jars manually.

Python Session Example

import httpcloak

with httpcloak.Session(preset="chrome-143") as session:
    # Login - cookies saved automatically
    session.post("https://example.com/login", json={
        "user": "myuser",
        "pass": "mypass"
    })
    
    # Dashboard request includes session cookies
    r = session.get("https://example.com/dashboard")
    print(r.json())

The with statement ensures the session closes properly. This is important for releasing network connections.

Node.js Session Example

const httpcloak = require("httpcloak");

async function scrapeWithSession() {
    const session = new httpcloak.Session({ preset: "chrome-143" });
    
    // Login
    await session.post("https://example.com/login", {
        json: { user: "myuser", pass: "mypass" }
    });
    
    // Authenticated request
    const r = await session.get("https://example.com/dashboard");
    console.log(r.json());
    
    session.close();
}

scrapeWithSession();

Step 5: Configure Proxy Support

For large-scale scraping, rotating proxies prevent IP-based blocking. httpcloak supports HTTP and SOCKS5 proxies.

Go with Proxy

c := client.NewClient("chrome-143",
    client.WithProxy("http://user:pass@proxy.example.com:8080"),
    client.WithTimeout(30*time.Second),
)
defer c.Close()

resp, err := c.Get(context.Background(), "https://target-site.com/data", nil)

The proxy URL format supports authentication: http://username:password@host:port. SOCKS5 proxies use socks5:// instead of http://.

Python with Proxy

import httpcloak

httpcloak.configure(
    preset="chrome-143",
    proxy="http://user:pass@proxy.example.com:8080",
    timeout=30
)

r = httpcloak.get("https://target-site.com/data")

The configure() function sets global defaults. All subsequent requests use these settings unless overridden.

Session with Proxy (Python)

session = httpcloak.Session(
    preset="chrome-143",
    proxy="http://user:pass@proxy.example.com:8080",
    timeout=30,
    retry=3,
    retry_on_status=[429, 500, 502, 503, 504]
)

The retry and retry_on_status options automatically retry failed requests. This handles temporary failures without manual intervention.

Step 6: Control Protocol and Redirect Behavior

Sometimes you need to force a specific protocol or disable redirects. httpcloak provides options for both.

Force HTTP/2 (Skip HTTP/3 Attempt)

c := client.NewClient("chrome-143", client.WithForceHTTP2())

HTTP/3 uses UDP, which some corporate networks block. Forcing HTTP/2 ensures TCP-based connections.

Force HTTP/1.1

c := client.NewClient("chrome-143", client.WithForceHTTP1())

Some legacy servers don't support HTTP/2. This option falls back to HTTP/1.1.

Disable Redirects

c := client.NewClient("chrome-143", client.WithoutRedirects())

When scraping, you might want to capture redirect responses instead of following them. This option returns the 3xx response directly.

Custom Redirect Limit

c := client.NewClient("chrome-143", client.WithRedirects(true, 5))

The default redirect limit is usually 10. Lowering it prevents infinite redirect loops from hanging your scraper.

Step 7: Handle File Uploads

Some scraping scenarios require uploading files—for example, submitting forms or interacting with APIs.

Python Multipart Upload

import httpcloak

# Upload a file
with open("document.pdf", "rb") as f:
    r = httpcloak.post("https://api.example.com/upload", files={
        "file": f
    })

print(r.json())

The files= parameter creates a multipart/form-data request automatically.

Upload with Custom Filename

# Specify filename and content type
r = httpcloak.post("https://api.example.com/upload", files={
    "file": ("custom_name.pdf", file_bytes, "application/pdf")
})

The tuple format lets you override the filename and MIME type sent to the server.

Upload with Form Data

r = httpcloak.post("https://api.example.com/upload",
    data={"description": "Q4 Report"},
    files={"file": open("report.pdf", "rb")}
)

Combining data= and files= creates a multipart request with both text fields and file attachments.

Available Browser Presets

httpcloak includes presets for multiple browsers and platforms:

Preset Browser Post-Quantum HTTP/2 HTTP/3
chrome-143 Chrome 143 X25519MLKEM768 Yes Yes
chrome-143-windows Chrome 143 (Windows) X25519MLKEM768 Yes Yes
chrome-143-linux Chrome 143 (Linux) X25519MLKEM768 Yes Yes
chrome-143-macos Chrome 143 (macOS) X25519MLKEM768 Yes Yes
chrome-131 Chrome 131 X25519MLKEM768 Yes Yes
firefox-133 Firefox 133 X25519 Yes No
safari-18 Safari 18 X25519 Yes No

Recommendation: Use chrome-143 for most cases. It has the widest compatibility and includes HTTP/3 plus post-quantum support.

Response Object Reference

All languages return response objects with similar properties:

Go Response

resp.StatusCode    // int
resp.Headers       // map[string]string
resp.Body          // []byte
resp.Text()        // string
resp.FinalURL      // string (after redirects)
resp.Protocol      // "h1", "h2", or "h3"

Python Response

r.status_code      # int
r.headers          # dict
r.content          # bytes
r.text             # str
r.json()           # parsed JSON
r.url              # final URL after redirects
r.protocol         # "h1", "h2", or "h3"
r.ok               # True if status < 400
r.raise_for_status()  # raises on 4xx/5xx

Node.js Response

r.statusCode       // number
r.headers          // object
r.content          // Buffer
r.text             // string
r.json()           // parsed JSON
r.url              // final URL after redirects
r.protocol         // "h1", "h2", or "h3"
r.ok               // true if status < 400
r.raiseForStatus() // throws on 4xx/5xx

Common Pitfalls and How to Avoid Them

Pitfall 1: Forgetting to Close Clients

Every client and session holds network connections. Failing to close them causes resource leaks.

Bad:

c := client.NewClient("chrome-143")
resp, _ := c.Get(ctx, url, nil)
// Connection never closed

Good:

c := client.NewClient("chrome-143")
defer c.Close()
resp, _ := c.Get(ctx, url, nil)

Pitfall 2: Mismatched User-Agent and Fingerprint

If you override the User-Agent header but it doesn't match the fingerprint preset, some systems detect the inconsistency.

Bad:

c := client.NewClient("chrome-143")
resp, _ := c.Do(ctx, &client.Request{
    Headers: map[string]string{
        "User-Agent": "Mozilla/5.0 Firefox/120.0",  // Firefox UA with Chrome fingerprint
    },
})

Good: Let httpcloak set the User-Agent automatically, or match it to your preset.

Pitfall 3: Using HTTP Proxies with HTTP/3

HTTP/3 uses QUIC over UDP. HTTP proxies only tunnel TCP connections. httpcloak automatically falls back to HTTP/2 when you use an HTTP proxy, but SOCKS5 proxies support UDP and work with HTTP/3.

When to Use httpcloak vs Other Tools

Use httpcloak when:

  • You need lightweight HTTP requests without browser overhead
  • Your target doesn't require JavaScript rendering
  • You're building high-throughput scrapers (thousands of requests per minute)
  • You want fine-grained control over requests

Use browser automation (Puppeteer, Playwright) when:

  • The site requires JavaScript to render content
  • You need to solve CAPTCHAs
  • You're interacting with complex single-page applications

Use both together when:

  • You need JavaScript rendering but want to avoid browser fingerprinting detection
  • Some endpoints work without JS while others require it

For scenarios requiring proxy rotation at scale, consider Roundproxies for residential, datacenter, ISP, or mobile proxies.

Final Thoughts

httpcloak fills a critical gap in the Go ecosystem. Standard HTTP clients get blocked because their TLS and HTTP/2 fingerprints don't match real browsers. httpcloak fixes this at the protocol level.

The library works best for high-volume scraping where browser automation would be too slow or resource-intensive. It handles the hard problems—post-quantum cryptography, HTTP/3 fingerprinting, header ordering—so you can focus on your actual scraping logic.

Start with a simple GET request to Cloudflare's trace endpoint. Verify you see kex=X25519MLKEM768 and http=http/3 in the response. That confirms everything is working.

From there, build up to sessions, proxies, and custom configurations as your project requires.

FAQ

Does httpcloak work with Cloudflare-protected sites?

Yes. httpcloak passes Cloudflare's TLS fingerprinting checks by producing browser-identical JA3/JA4 hashes. However, Cloudflare uses multiple detection layers. You may still encounter JavaScript challenges that require browser rendering.

Can I use httpcloak with existing Go HTTP middleware?

httpcloak provides its own client interface rather than wrapping net/http. If you need standard library compatibility, you'll need to adapt your middleware to httpcloak's request/response types.

Why does HTTP/3 matter for fingerprinting?

HTTP/3 uses QUIC, which has its own fingerprint based on transport parameters. Sites that support HTTP/3 can fingerprint QUIC connections separately from TLS. httpcloak matches Chrome's QUIC fingerprint when HTTP/3 is used.

How often are browser presets updated?

The library maintainer updates presets when new browser versions ship with fingerprint changes. Check the GitHub repository for the latest presets.

Does httpcloak support connection pooling?

Yes. Clients automatically pool connections for reuse. This improves performance when making multiple requests to the same host.