Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
H

httpcloak

> 开发工具
Open source

Go HTTP client with browser-identical TLS/HTTP2 fingerprinting. Bypass bot detection by perfectly mimicking Chrome, Firefox, and Safari at the cryptographic lev

1.2K stars0 likes0 views
WebsiteGitHub

About

Go HTTP client with browser-identical TLS/HTTP2 fingerprinting. Bypass bot detection by perfectly mimicking Chrome, Firefox, and Safari at the cryptographic lev

Every Byte of your Request Indistinguishable from Chrome.

Full documentation at httpcloak.dev


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-latest")

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
  • TCP/IP stack (TTL, MSS, Window)

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:            +    │
└─────────────────────────────────┘

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-latest")
print(r.status_code, r.protocol)

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

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

Go

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

// Simple request
c := client.NewClient("chrome-latest")
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-latest" });
const r1 = await session.get("https://example.com");
console.log(r1.statusCode, r1.protocol);

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

// Custom headers
const r3 = 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.Chrome145);
var r1 = session.Get("https://example.com");
Console.WriteLine($"{r1.StatusCode} {r1.Protocol}");

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

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

Features

Build Any Browser Fingerprint From JSON

Don't have a preset for your target browser? Capture once, use forever. Visit tls.peet.ws/api/all in the browser you want to mimic, paste the JA3 + Akamai fingerprint into a JSON spec, register it, and you have a brand-new preset that emits real wire bytes.

…

describe_preset emits every effective field — TLS extensions, HTTP/2 SETTINGS order, HPACK encoding order, per-resource-type stream priority table, QUIC transport params, TCP/IP fingerprint, full header set — so anything you see in the JSON is editable. Mutated specs round-trip byte-equal through load_preset_from_json → run → describe_preset: same wire mechanics, just the values you changed.

Same workflow across all bindings:

Describe Load Unregister
Python httpcloak.describe_preset(name) httpcloak.load_preset_from_json(json) httpcloak.unregister_preset(name)
Node.js describePreset(name) loadPresetFromJSON(json) unregisterPreset(name)
.NET CustomPresets.Describe(name) CustomPresets.LoadFromJson(json) CustomPresets.Unregister(name)
Go fingerprint.Describe(name) fingerprint.LoadPresetFromJSON(json) fingerprint.Unregister(name)

Pool dozens of fingerprints with PresetPool (round-robin / random rotation, all bindings). Drill-down recipes — bumping a single H2 priority, inserting an HPACK header, importing a peet.ws capture, cleaning up — in examples/python-examples/17_tweak_fingerprint.py, examples/js-examples/18_tweak_fingerprint.js, and examples/csharp-examples/TweakFingerprint.cs.

ECH (Encrypted Client Hello)

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

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

Cloudflare trace shows sni=encrypted instead of sni=plaintext. Use cloudflare-ech.com (the dedicated ECH domain) for any Cloudflare-fronted target.

⚡ Session Resumption (0-RTT)

TLS session tickets make you look like a returning visitor.

# Warm up on any Cloudflare site
session.get("https://cloudflare.com/")
session.save("session.json")

# Use on your target
session = httpcloak.Session.load("session.json")
r = session.get("https://target.com/")  # Bot score: 99

Cross-domain warming works because Cloudflare sites share TLS infrastructure.

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.

Speculative TLS (opt-in): CONNECT + TLS ClientHello are sent together, saving one proxy round-trip (~25% faster). Enable for compatible proxies:

session = httpcloak.Session(proxy="socks5://...", enable_speculative_tls=True)

Domain Fronting

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

session := httpcloak.NewSession("chrome-latest",
    httpcloak.WithConnectTo("public-cdn.com", "actual-backend.internal"),
)
defer session.Close()

Certificate Pinning & Verification

Pin a key, or take verification over entirely with your own callback:

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

// Pin an SPKI SHA-256, scoped to one host
c.PinCertificate("sha256/AAAA...", client.ForHost("api.example.com"), client.IncludeSubdomains())

Or run your own checks, mirroring crypto/tls:

session := httpcloak.NewSession("chrome-latest",
    httpcloak.WithVerifyPeerCertificate(func(rawCerts [][]byte, chains [][]*x509.Certificate) error {
        // Return an error to abort the handshake.
        return myPinCheck(rawCerts)
    }),
)
defer session.Close()

WithVerifyConnection gives you the completed tls.ConnectionState. WithTLSConfig accepts a standard *tls.Config but reads only its verification fields: anything that would reshape the ClientHello is ignored on purpose, since that would change how you look on the wire.

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-latest", http_version="h3")  # Force HTTP/3
session = httpcloak.Session(preset="chrome-latest", http_version="h2")  # Force HTTP/2
session = httpcloak.Session(preset="chrome-latest", http_version="h1")  # Force HTTP/1.1

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

️ TCP/IP Fingerprinting

Anti-bot systems inspect TCP SYN packet parameters (TTL, Window Size, MSS, Window Scale) to verify your claimed OS matches. A request claiming Chrome on Windows but with Linux TCP parameters (TTL=64) is instantly flagged.

httpcloak automatically sets the correct TCP/IP fingerprint for each preset's platform. You can also override manually:

session = httpcloak.Session(
    preset="chrome-latest-windows",
    tcp_ttl=128,           # Windows=128, Linux/macOS=64
    tcp_window_size=64240, # Windows=64240, Linux/macOS=65535
    tcp_window_scale=8,    # Windows=8, Linux=7, macOS=6
    tcp_mss=1460,          # Standard Ethernet MTU
)

Go:

session := httpcloak.NewSession("chrome-latest-windows",
    httpcloak.WithTCPFingerprint(fingerprint.TCPFingerprint{
        TTL: 128, MSS: 1460, WindowSize: 64240, WindowScale: 8, DFBit: true,
    }),
)
defer session.Close()

Node.js:

const session = new httpcloak.Session({
    preset: "chrome-latest-windows",
    tcpTtl: 128,
    tcpWindowSize: 64240,
    tcpWindowScale: 8,
});

C#:

var session = new Session(
    preset: Presets.Chrome145Windows,
    tcpTtl: 128,
    tcpWindowSize: 64240,
    tcpWindowScale: 8
);

Built-in platform profiles: Windows (TTL=128, WS=8), Linux (TTL=64, WS=7), macOS (TTL=64, WS=6).

Runtime Proxy Switching

Switch proxies mid-session without creating new connections. Perfect for proxy rotation.

session = httpcloak.Session(preset="chrome-latest")

# Start with direct connection
r = session.get("https://api.ipify.org")
print(f"Direct IP: {r.text}")

# Switch to proxy 1
session.set_proxy("http://proxy1.example.com:8080")
r = session.get("https://api.ipify.org")
print(f"Proxy 1 IP: {r.text}")

# Switch to proxy 2
session.set_proxy("socks5://proxy2.example.com:1080")
r = session.get("https://api.ipify.org")
print(f"Proxy 2 IP: {r.text}")

# Back to direct
session.set_proxy("")

Split proxy configuration - use different proxies for HTTP/2 and HTTP/3:

session = httpcloak.Session(preset="chrome-latest")

# TCP proxy for HTTP/1.1 and HTTP/2
session.set_tcp_proxy("http://tcp-proxy.example.com:8080")

# UDP proxy for HTTP/3 (requires SOCKS5 UDP ASSOCIATE or MASQUE)
session.set_udp_proxy("socks5://udp-proxy.example.com:1080")

# Check current configuration
print(session.get_tcp_proxy())  # TCP proxy URL
print(session.get_udp_proxy())  # UDP proxy URL

Header Order Customization

A partial list is a prefix, not a replacement. The names you pass are emitted first, in your order; every header you leave out keeps its normal position from the preset's own table, and anything still unplaced follows sorted by name. So naming one header costs you nothing for the rest. Passing a complete list gives you exactly

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Goanti-botbot-detectionbrowser-fingerprintbrowser-fingerprinting

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category开发工具
PricingOpen source

> Related tools

V
VS Code
流行的开源代码编辑器
G
Git
分布式版本控制系统
V
Vite
下一代前端构建工具