Skip to content

Go HTTP client with browser-identical TLS/HTTP2 fingerprinting. Bypass bot detection by perfectly mimicking Chrome, Firefox, and Safari at the cryptographic level (JA3/JA4, Akamai fingerprint, header order). Supports HTTP/1.1, HTTP/2, HTTP/3, sessions, cookies, and proxies.

License

Notifications You must be signed in to change notification settings

sardanioss/httpcloak

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

httpcloak

Go Reference PyPI npm NuGet

Every Byte of your Request Indistinguishable from Chrome.



The Problem

Bot detection doesn't just check your User-Agent anymore.

It fingerprints your TLS handshake. Your HTTP/2 frames. Your QUIC parameters. The order of your headers. Whether your SNI is encrypted.

One mismatch = blocked.

The Solution

import httpcloak

r = httpcloak.get("https://target.com", preset="chrome-143")

That's it. Full browser transport layer fingerprint.


What Gets Emulated

πŸ” TLS Layer

  • JA3 / JA4 fingerprints
  • GREASE randomization
  • Post-quantum X25519MLKEM768
  • ECH (Encrypted Client Hello)

πŸš€ Transport Layer

  • HTTP/2 SETTINGS frames
  • WINDOW_UPDATE values
  • Stream priorities (HPACK)
  • QUIC transport parameters
  • HTTP/3 GREASE frames

🧠 Header Layer

  • Sec-Fetch-* coherence
  • Client Hints (Sec-Ch-UA)
  • Accept / Accept-Language
  • Header ordering
  • Cookie persistence

Results

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ECH (Encrypted Client Hello)   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  WITHOUT:  sni=plaintext        β”‚
β”‚  WITH:     sni=encrypted   +    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  HTTP/3 Fingerprint Match       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Protocol:        h3       +    β”‚
β”‚  QUIC Version:    1        +    β”‚
β”‚  Transport Params:         +    β”‚
β”‚  GREASE Frames:            +    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

vs curl_cffi

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        BOTH LIBRARIES          β”‚       HTTPCLOAK ONLY           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                β”‚                                β”‚
β”‚  + TLS fingerprint (JA3/JA4)   β”‚  + HTTP/3 fingerprinting       β”‚
β”‚  + HTTP/2 fingerprint          β”‚  + ECH (encrypted SNI)         β”‚
β”‚  + Post-quantum TLS            β”‚  + MASQUE proxy                β”‚
β”‚  + Bot score: 99               β”‚  + Domain fronting             β”‚
β”‚                                β”‚  + Certificate pinning         β”‚
β”‚                                β”‚  + Go, Python, Node.js, C#     β”‚
β”‚                                β”‚                                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Install

pip install httpcloak        # Python
npm install httpcloak        # Node.js
go get github.com/sardanioss/httpcloak   # Go
dotnet add package HttpCloak # C#

Quick Start

Python

import httpcloak

# Simple request
r = httpcloak.get("https://example.com", preset="chrome-143")
print(r.status_code, r.protocol)

# POST with JSON
r = httpcloak.post("https://httpbin.org/post",
    json={"key": "value"},
    preset="chrome-143"
)

# Custom headers
r = httpcloak.get("https://httpbin.org/headers",
    headers={"X-Custom": "value"},
    preset="chrome-143"
)

Go

import (
    "context"
    "github.com/sardanioss/httpcloak/client"
)

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

resp, _ := c.Get(ctx, "https://example.com", nil)
body, _ := resp.Text()
fmt.Println(resp.StatusCode, resp.Protocol)

// POST with JSON
jsonBody := []byte(`{"key": "value"}`)
resp, _ = c.Post(ctx, "https://httpbin.org/post",
    bytes.NewReader(jsonBody),
    map[string][]string{"Content-Type": {"application/json"}},
)

// Custom headers
resp, _ = c.Get(ctx, "https://httpbin.org/headers", map[string][]string{
    "X-Custom": {"value"},
})

Node.js

import httpcloak from "httpcloak";

// Simple request
const session = new httpcloak.Session({ preset: "chrome-143" });
const r = await session.get("https://example.com");
console.log(r.statusCode, r.protocol);

// POST with JSON
const r = await session.post("https://httpbin.org/post", {
    json: { key: "value" }
});

// Custom headers
const r = await session.get("https://httpbin.org/headers", {
    headers: { "X-Custom": "value" }
});

session.close();

C#

using HttpCloak;

// Simple request
using var session = new Session(preset: Presets.Chrome143);
var r = session.Get("https://example.com");
Console.WriteLine($"{r.StatusCode} {r.Protocol}");

// POST with JSON
var r = session.Post("https://httpbin.org/post",
    json: new { key = "value" }
);

// Custom headers
var r = session.Get("https://httpbin.org/headers",
    headers: new Dictionary<string, string> { ["X-Custom"] = "value" }
);

Features

πŸ” ECH (Encrypted Client Hello)

Hides which domain you're connecting to from network observers.

session = httpcloak.Session(
    preset="chrome-143",
    ech_from="cloudflare.com"  # Fetches ECH config from DNS
)

Cloudflare trace shows sni=encrypted instead of sni=plaintext.

🌐 HTTP/3 Through Proxies

Two methods for QUIC through proxies:

Method How it works
SOCKS5 UDP ASSOCIATE Proxy relays UDP packets. Most residential proxies support this.
MASQUE (CONNECT-UDP) RFC 9298. Tunnels UDP over HTTP/3. Premium providers only.
# SOCKS5 with UDP
session = httpcloak.Session(proxy="socks5://user:pass@proxy:1080")

# MASQUE
session = httpcloak.Session(proxy="masque://proxy:443")

Known MASQUE providers (auto-detected): Bright Data, Oxylabs, Smartproxy, SOAX.

🎭 Domain Fronting

Connect to a different host than what appears in TLS SNI.

client := httpcloak.NewClient("chrome-143",
    httpcloak.WithConnectTo("public-cdn.com", "actual-backend.internal"),
)

πŸ“Œ Certificate Pinning

client.PinCertificate("sha256/AAAA...",
    httpcloak.PinOptions{IncludeSubdomains: true})

πŸͺ Request Hooks

client.OnPreRequest(func(req *http.Request) error {
    req.Header.Set("X-Custom", "value")
    return nil
})

client.OnPostResponse(func(resp *httpcloak.Response) {
    log.Printf("Got %d from %s", resp.StatusCode, resp.FinalURL)
})

⏱️ Request Timing

fmt.Printf("DNS: %dms, TCP: %dms, TLS: %dms, Total: %dms\n",
    resp.Timing.DNSLookup,
    resp.Timing.TCPConnect,
    resp.Timing.TLSHandshake,
    resp.Timing.Total)

πŸ”„ Protocol Selection

session = httpcloak.Session(preset="chrome-143", http_version="h3")  # Force HTTP/3
session = httpcloak.Session(preset="chrome-143", http_version="h2")  # Force HTTP/2
session = httpcloak.Session(preset="chrome-143", http_version="h1")  # Force HTTP/1.1

Auto mode tries HTTP/3 first, falls back gracefully.

πŸ“€ Streaming & Uploads

# Stream large downloads
stream = session.get_stream("https://example.com/large-file.zip")
print(f"Size: {stream.content_length} bytes")

with open("file.zip", "wb") as f:
    while True:
        chunk = stream.read(8192)
        if not chunk:
            break
        f.write(chunk)
stream.close()

# Iterator pattern
for chunk in session.get_stream(url).iter_content(chunk_size=8192):
    process(chunk)

# Multipart upload
r = session.post(url, files={
    "file": ("filename.jpg", file_bytes, "image/jpeg")
})

πŸ”’ Authentication

# Basic auth
r = httpcloak.get("https://api.example.com/data",
    auth=("username", "password"),
    preset="chrome-143"
)

# Session-level auth
session = httpcloak.Session(
    preset="chrome-143",
    auth=("username", "password")
)

⏰ Timeouts & Retries

# Timeout
session = httpcloak.Session(preset="chrome-143", timeout=30)

# Per-request timeout
r = session.get("https://slow-api.com/data", timeout=60)
// Go: Timeout and retry configuration
client := client.NewClient("chrome-143",
    client.WithTimeout(30 * time.Second),
    client.WithRetry(3),  // Retry 3 times on 429, 500, 502, 503, 504
    client.WithRetryConfig(
        5,                      // Max retries
        500 * time.Millisecond, // Min backoff
        10 * time.Second,       // Max backoff
        []int{429, 503},        // Status codes to retry
    ),
)

🚫 Redirect Control

// Disable automatic redirects
client := client.NewClient("chrome-143",
    client.WithoutRedirects(),
)

resp, _ := client.Get(ctx, "https://example.com/redirect", nil)
fmt.Println(resp.StatusCode)              // 302
fmt.Println(resp.GetHeader("location"))   // Redirect URL

API Reference

Python

import httpcloak

# Module-level functions
httpcloak.get(url, **kwargs)
httpcloak.post(url, **kwargs)
httpcloak.put(url, **kwargs)
httpcloak.patch(url, **kwargs)
httpcloak.delete(url, **kwargs)
httpcloak.head(url, **kwargs)
httpcloak.options(url, **kwargs)

# Session class
session = httpcloak.Session(
    preset="chrome-143",       # Browser preset
    proxy="socks5://...",      # Proxy URL
    timeout=30,                # Timeout in seconds
    http_version="h3",         # Force protocol: h1, h2, h3, auto
    ech_from="cloudflare.com", # ECH config source
    auth=("user", "pass"),     # Basic auth
)

# Session methods
session.get(url, **kwargs)
session.post(url, data=None, json=None, **kwargs)
session.get_stream(url)        # Streaming download
session.close()

# Response object
response.status_code           # HTTP status
response.ok                    # True if status < 400
response.text                  # Body as string
response.content               # Body as bytes
response.json()                # Parse JSON
response.headers               # Response headers
response.protocol              # h1, h2, or h3
response.url                   # Final URL
response.raise_for_status()    # Raise on 4xx/5xx

Go

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

// Client creation
c := client.NewClient("chrome-143",
    client.WithTimeout(30 * time.Second),
    client.WithProxy("socks5://..."),
    client.WithRetry(3),
    client.WithoutRedirects(),
    client.WithInsecureSkipVerify(),
)
defer c.Close()

// Request methods
resp, err := c.Get(ctx, url, headers)
resp, err := c.Post(ctx, url, body, headers)
resp, err := c.Put(ctx, url, body, headers)
resp, err := c.Delete(ctx, url, headers)

// Advanced request
resp, err := c.Do(ctx, &client.Request{
    Method:        "GET",
    URL:           url,
    Headers:       map[string][]string{},
    Body:          io.Reader,
    Params:        map[string]string{},
    ForceProtocol: client.ProtocolHTTP3,
    FetchMode:     client.FetchModeCORS,
    Referer:       "https://example.com",
})

// Response object
resp.StatusCode
resp.Protocol
resp.Headers
resp.Body           // io.ReadCloser
resp.Text()         // (string, error)
resp.Bytes()        // ([]byte, error)
resp.JSON(&v)       // error
resp.GetHeader(key) // string
resp.IsSuccess()    // bool
resp.IsRedirect()   // bool

Node.js

import httpcloak from "httpcloak";

// Session creation
const session = new httpcloak.Session({
    preset: "chrome-143",
    proxy: "socks5://...",
    timeout: 30000,
    httpVersion: "h3",
});

// Async methods
await session.get(url, options)
await session.post(url, { json, data, headers })
await session.put(url, options)
await session.delete(url, options)

// Sync methods
session.getSync(url, options)
session.postSync(url, options)
session.close()

// Response object
response.statusCode
response.ok
response.text
response.json()
response.headers
response.protocol

C#

using HttpCloak;

// Session creation
var session = new Session(
    preset: Presets.Chrome143,
    proxy: "socks5://...",
    timeout: 30
);

// Request methods
session.Get(url, headers)
session.Post(url, json: obj, data: dict, headers: dict)
session.Put(url, ...)
session.Delete(url)
session.Dispose()

// Response object
response.StatusCode
response.Ok
response.Text
response.Json<T>()
response.Headers
response.Protocol

Browser Presets

Preset Platform PQ H3
chrome-143 Auto βœ… βœ…
chrome-143-windows Windows βœ… βœ…
chrome-143-macos macOS βœ… βœ…
chrome-143-linux Linux βœ… βœ…
firefox-133 Auto ❌ ❌
chrome-mobile-android Android βœ… βœ…
chrome-mobile-ios iOS βœ… βœ…

PQ = Post-Quantum (X25519MLKEM768) Β· H3 = HTTP/3


Testing Tools

Tool Tests
tls.peet.ws JA3, JA4, HTTP/2 Akamai
quic.browserleaks.com HTTP/3 QUIC fingerprint
cf.erisa.uk Cloudflare bot score
cloudflare.com/cdn-cgi/trace ECH status, TLS version

Dependencies

Custom forks for browser-accurate fingerprinting:


Connect


MIT License

About

Go HTTP client with browser-identical TLS/HTTP2 fingerprinting. Bypass bot detection by perfectly mimicking Chrome, Firefox, and Safari at the cryptographic level (JA3/JA4, Akamai fingerprint, header order). Supports HTTP/1.1, HTTP/2, HTTP/3, sessions, cookies, and proxies.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •