{"id":44068351,"url":"https://github.com/sardanioss/httpcloak","last_synced_at":"2026-02-20T01:00:50.684Z","repository":{"id":331215735,"uuid":"1124369965","full_name":"sardanioss/httpcloak","owner":"sardanioss","description":"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.","archived":false,"fork":false,"pushed_at":"2026-02-15T17:08:23.000Z","size":41249,"stargazers_count":742,"open_issues_count":8,"forks_count":58,"subscribers_count":6,"default_branch":"main","last_synced_at":"2026-02-15T18:47:01.962Z","etag":null,"topics":["anti-bot","bot-detection","browser-fingerprint","browser-fingerprinting","cloudflare","go","golang","http-client","http2","http3","ja3-fingerprint","ja4-fingerprint","js","nodejs","python","python3","quic","tls-fingerprint","tls-fingerprinting","web-scraping"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sardanioss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-28T22:39:27.000Z","updated_at":"2026-02-15T17:08:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sardanioss/httpcloak","commit_stats":null,"previous_names":["sardanioss/httpcloak"],"tags_count":39,"template":false,"template_full_name":null,"purl":"pkg:github/sardanioss/httpcloak","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sardanioss%2Fhttpcloak","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sardanioss%2Fhttpcloak/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sardanioss%2Fhttpcloak/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sardanioss%2Fhttpcloak/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sardanioss","download_url":"https://codeload.github.com/sardanioss/httpcloak/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sardanioss%2Fhttpcloak/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29637914,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-19T22:32:43.237Z","status":"ssl_error","status_checked_at":"2026-02-19T22:32:38.330Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anti-bot","bot-detection","browser-fingerprint","browser-fingerprinting","cloudflare","go","golang","http-client","http2","http3","ja3-fingerprint","ja4-fingerprint","js","nodejs","python","python3","quic","tls-fingerprint","tls-fingerprinting","web-scraping"],"created_at":"2026-02-08T04:00:24.664Z","updated_at":"2026-02-20T01:00:50.678Z","avatar_url":"https://github.com/sardanioss.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"httpcloak.png\" alt=\"httpcloak\" width=\"600\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://pkg.go.dev/github.com/sardanioss/httpcloak\"\u003e\u003cimg src=\"https://pkg.go.dev/badge/github.com/sardanioss/httpcloak.svg\" alt=\"Go Reference\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://pypi.org/project/httpcloak/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/v/httpcloak\" alt=\"PyPI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/httpcloak\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/httpcloak\" alt=\"npm\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.nuget.org/packages/HttpCloak\"\u003e\u003cimg src=\"https://img.shields.io/nuget/v/HttpCloak\" alt=\"NuGet\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ci\u003eEvery Byte of your Request Indistinguishable from Chrome.\u003c/i\u003e\n\u003c/p\u003e\n\n\u003cbr\u003e\n\n---\n\n## The Problem\n\nBot detection doesn't just check your User-Agent anymore.\n\nIt fingerprints your **TLS handshake**. Your **HTTP/2 frames**. Your **QUIC parameters**. The order of your headers. Whether your SNI is encrypted.\n\nOne mismatch = blocked.\n\n## The Solution\n\n```python\nimport httpcloak\n\nr = httpcloak.get(\"https://target.com\", preset=\"chrome-144\")\n```\n\nThat's it. Full browser transport layer fingerprint.\n\n---\n\n## What Gets Emulated\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd width=\"33%\" valign=\"top\"\u003e\n\n### 🔐 TLS Layer\n\n- JA3 / JA4 fingerprints\n- GREASE randomization\n- Post-quantum X25519MLKEM768\n- ECH (Encrypted Client Hello)\n\n\u003c/td\u003e\n\u003ctd width=\"33%\" valign=\"top\"\u003e\n\n### 🚀 Transport Layer\n\n- HTTP/2 SETTINGS frames\n- WINDOW_UPDATE values\n- Stream priorities (HPACK)\n- QUIC transport parameters\n- HTTP/3 GREASE frames\n\n\u003c/td\u003e\n\u003ctd width=\"33%\" valign=\"top\"\u003e\n\n### 🧠 Header Layer\n\n- Sec-Fetch-* coherence\n- Client Hints (Sec-Ch-UA)\n- Accept / Accept-Language\n- Header ordering\n- Cookie persistence\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n---\n\n## Results\n\n```\n┌─────────────────────────────────┐\n│  ECH (Encrypted Client Hello)   │\n├─────────────────────────────────┤\n│  WITHOUT:  sni=plaintext        │\n│  WITH:     sni=encrypted   +    │\n└─────────────────────────────────┘\n```\n\n```\n┌─────────────────────────────────┐\n│  HTTP/3 Fingerprint Match       │\n├─────────────────────────────────┤\n│  Protocol:        h3       +    │\n│  QUIC Version:    1        +    │\n│  Transport Params:         +    │\n│  GREASE Frames:            +    │\n└─────────────────────────────────┘\n```\n\n---\n\n## vs curl_cffi\n\n```\n┌────────────────────────────────┬────────────────────────────────┐\n│        BOTH LIBRARIES          │       HTTPCLOAK ONLY           │\n├────────────────────────────────┼────────────────────────────────┤\n│                                │                                │\n│  + TLS fingerprint (JA3/JA4)   │  + HTTP/3 fingerprinting       │\n│  + HTTP/2 fingerprint          │  + ECH (encrypted SNI)         │\n│  + Post-quantum TLS            │  + MASQUE proxy                │\n│  + Bot score: 99               │  + Domain fronting             │\n│                                │  + Certificate pinning         │\n│                                │  + Go, Python, Node.js, C#     │\n│                                │                                │\n└────────────────────────────────┴────────────────────────────────┘\n```\n\n---\n\n## Install\n\n```bash\npip install httpcloak        # Python\nnpm install httpcloak        # Node.js\ngo get github.com/sardanioss/httpcloak   # Go\ndotnet add package HttpCloak # C#\n```\n\n---\n\n## Quick Start\n\n### Python\n\n```python\nimport httpcloak\n\n# Simple request\nr = httpcloak.get(\"https://example.com\", preset=\"chrome-144\")\nprint(r.status_code, r.protocol)\n\n# POST with JSON\nr = httpcloak.post(\"https://httpbin.org/post\",\n    json={\"key\": \"value\"},\n    preset=\"chrome-144\"\n)\n\n# Custom headers\nr = httpcloak.get(\"https://httpbin.org/headers\",\n    headers={\"X-Custom\": \"value\"},\n    preset=\"chrome-144\"\n)\n```\n\n### Go\n\n```go\nimport (\n    \"context\"\n    \"github.com/sardanioss/httpcloak/client\"\n)\n\n// Simple request\nc := client.NewClient(\"chrome-144\")\ndefer c.Close()\n\nresp, _ := c.Get(ctx, \"https://example.com\", nil)\nbody, _ := resp.Text()\nfmt.Println(resp.StatusCode, resp.Protocol)\n\n// POST with JSON\njsonBody := []byte(`{\"key\": \"value\"}`)\nresp, _ = c.Post(ctx, \"https://httpbin.org/post\",\n    bytes.NewReader(jsonBody),\n    map[string][]string{\"Content-Type\": {\"application/json\"}},\n)\n\n// Custom headers\nresp, _ = c.Get(ctx, \"https://httpbin.org/headers\", map[string][]string{\n    \"X-Custom\": {\"value\"},\n})\n```\n\n### Node.js\n\n```javascript\nimport httpcloak from \"httpcloak\";\n\n// Simple request\nconst session = new httpcloak.Session({ preset: \"chrome-144\" });\nconst r = await session.get(\"https://example.com\");\nconsole.log(r.statusCode, r.protocol);\n\n// POST with JSON\nconst r = await session.post(\"https://httpbin.org/post\", {\n    json: { key: \"value\" }\n});\n\n// Custom headers\nconst r = await session.get(\"https://httpbin.org/headers\", {\n    headers: { \"X-Custom\": \"value\" }\n});\n\nsession.close();\n```\n\n### C#\n\n```csharp\nusing HttpCloak;\n\n// Simple request\nusing var session = new Session(preset: Presets.Chrome144);\nvar r = session.Get(\"https://example.com\");\nConsole.WriteLine($\"{r.StatusCode} {r.Protocol}\");\n\n// POST with JSON\nvar r = session.PostJson(\"https://httpbin.org/post\",\n    new { key = \"value\" }\n);\n\n// Custom headers\nvar r = session.Get(\"https://httpbin.org/headers\",\n    headers: new Dictionary\u003cstring, string\u003e { [\"X-Custom\"] = \"value\" }\n);\n```\n\n---\n\n## Features\n\n### 🔐 ECH (Encrypted Client Hello)\n\nHides which domain you're connecting to from network observers.\n\n```python\nsession = httpcloak.Session(\n    preset=\"chrome-144\",\n    ech_from=\"cloudflare.com\"  # Fetches ECH config from DNS\n)\n```\n\nCloudflare trace shows `sni=encrypted` instead of `sni=plaintext`.\n\n### ⚡ Session Resumption (0-RTT)\n\nTLS session tickets make you look like a returning visitor.\n\n```python\n# Warm up on any Cloudflare site\nsession.get(\"https://cloudflare.com/\")\nsession.save(\"session.json\")\n\n# Use on your target\nsession = httpcloak.Session.load(\"session.json\")\nr = session.get(\"https://target.com/\")  # Bot score: 99\n```\n\nCross-domain warming works because Cloudflare sites share TLS infrastructure.\n\n### 🌐 HTTP/3 Through Proxies\n\nTwo methods for QUIC through proxies:\n\n| Method | How it works |\n|--------|--------------|\n| **SOCKS5 UDP ASSOCIATE** | Proxy relays UDP packets. Most residential proxies support this. |\n| **MASQUE (CONNECT-UDP)** | RFC 9298. Tunnels UDP over HTTP/3. Premium providers only. |\n\n```python\n# SOCKS5 with UDP\nsession = httpcloak.Session(proxy=\"socks5://user:pass@proxy:1080\")\n\n# MASQUE\nsession = httpcloak.Session(proxy=\"masque://proxy:443\")\n```\n\nKnown MASQUE providers (auto-detected): Bright Data, Oxylabs, Smartproxy, SOAX.\n\n**Speculative TLS** (enabled by default): CONNECT + TLS ClientHello are sent together, saving one proxy round-trip (~25% faster). Disable if you experience issues with certain proxies:\n\n```python\nsession = httpcloak.Session(proxy=\"socks5://...\", disable_speculative_tls=True)\n```\n\n### 🎭 Domain Fronting\n\nConnect to a different host than what appears in TLS SNI.\n\n```go\nclient := httpcloak.NewClient(\"chrome-144\",\n    httpcloak.WithConnectTo(\"public-cdn.com\", \"actual-backend.internal\"),\n)\n```\n\n### 📌 Certificate Pinning\n\n```go\nclient.PinCertificate(\"sha256/AAAA...\",\n    httpcloak.PinOptions{IncludeSubdomains: true})\n```\n\n### 🪝 Request Hooks\n\n```go\nclient.OnPreRequest(func(req *http.Request) error {\n    req.Header.Set(\"X-Custom\", \"value\")\n    return nil\n})\n\nclient.OnPostResponse(func(resp *httpcloak.Response) {\n    log.Printf(\"Got %d from %s\", resp.StatusCode, resp.FinalURL)\n})\n```\n\n### ⏱️ Request Timing\n\n```go\nfmt.Printf(\"DNS: %dms, TCP: %dms, TLS: %dms, Total: %dms\\n\",\n    resp.Timing.DNSLookup,\n    resp.Timing.TCPConnect,\n    resp.Timing.TLSHandshake,\n    resp.Timing.Total)\n```\n\n### 🔄 Protocol Selection\n\n```python\nsession = httpcloak.Session(preset=\"chrome-144\", http_version=\"h3\")  # Force HTTP/3\nsession = httpcloak.Session(preset=\"chrome-144\", http_version=\"h2\")  # Force HTTP/2\nsession = httpcloak.Session(preset=\"chrome-144\", http_version=\"h1\")  # Force HTTP/1.1\n```\n\nAuto mode tries HTTP/3 first, falls back gracefully.\n\n### 🔀 Runtime Proxy Switching\n\nSwitch proxies mid-session without creating new connections. Perfect for proxy rotation.\n\n```python\nsession = httpcloak.Session(preset=\"chrome-144\")\n\n# Start with direct connection\nr = session.get(\"https://api.ipify.org\")\nprint(f\"Direct IP: {r.text}\")\n\n# Switch to proxy 1\nsession.set_proxy(\"http://proxy1.example.com:8080\")\nr = session.get(\"https://api.ipify.org\")\nprint(f\"Proxy 1 IP: {r.text}\")\n\n# Switch to proxy 2\nsession.set_proxy(\"socks5://proxy2.example.com:1080\")\nr = session.get(\"https://api.ipify.org\")\nprint(f\"Proxy 2 IP: {r.text}\")\n\n# Back to direct\nsession.set_proxy(\"\")\n```\n\n**Split proxy configuration** - use different proxies for HTTP/2 and HTTP/3:\n\n```python\nsession = httpcloak.Session(preset=\"chrome-144\")\n\n# TCP proxy for HTTP/1.1 and HTTP/2\nsession.set_tcp_proxy(\"http://tcp-proxy.example.com:8080\")\n\n# UDP proxy for HTTP/3 (requires SOCKS5 UDP ASSOCIATE or MASQUE)\nsession.set_udp_proxy(\"socks5://udp-proxy.example.com:1080\")\n\n# Check current configuration\nprint(session.get_tcp_proxy())  # TCP proxy URL\nprint(session.get_udp_proxy())  # UDP proxy URL\n```\n\n### 📋 Header Order Customization\n\nControl the order headers are sent for advanced fingerprinting scenarios.\n\n```python\nsession = httpcloak.Session(preset=\"chrome-144\")\n\n# Get the current header order (from preset)\nprint(session.get_header_order())\n\n# Set custom header order\nsession.set_header_order([\n    \"accept-language\", \"sec-ch-ua\", \"accept\",\n    \"sec-fetch-site\", \"sec-fetch-mode\", \"user-agent\",\n    \"sec-ch-ua-platform\", \"sec-ch-ua-mobile\"\n])\n\n# Make request with custom order\nr = session.get(\"https://example.com\")\n\n# Reset to preset's default order\nsession.set_header_order([])\n```\n\n**JavaScript:**\n```javascript\nsession.setHeaderOrder([\"accept-language\", \"sec-ch-ua\", \"accept\", ...]);\nconsole.log(session.getHeaderOrder());\nsession.setHeaderOrder([]);  // Reset to default\n```\n\n**C#:**\n```csharp\nsession.SetHeaderOrder(new[] { \"accept-language\", \"sec-ch-ua\", \"accept\", ... });\nConsole.WriteLine(string.Join(\", \", session.GetHeaderOrder()));\nsession.SetHeaderOrder(null);  // Reset to default\n```\n\n**Go:**\n```go\nc.SetHeaderOrder([]string{\"accept-language\", \"sec-ch-ua\", \"accept\", ...})\nfmt.Println(c.GetHeaderOrder())\nc.SetHeaderOrder(nil)  // Reset to default\n```\n\n### 📤 Streaming \u0026 Uploads\n\n```python\n# Stream large downloads\nstream = session.get_stream(\"https://example.com/large-file.zip\")\nprint(f\"Size: {stream.content_length} bytes\")\n\nwith open(\"file.zip\", \"wb\") as f:\n    while True:\n        chunk = stream.read(8192)\n        if not chunk:\n            break\n        f.write(chunk)\nstream.close()\n\n# Iterator pattern\nfor chunk in session.get_stream(url).iter_content(chunk_size=8192):\n    process(chunk)\n\n# Multipart upload\nr = session.post(url, files={\n    \"file\": (\"filename.jpg\", file_bytes, \"image/jpeg\")\n})\n```\n\n### 🔒 Authentication\n\n```python\n# Basic auth\nr = httpcloak.get(\"https://api.example.com/data\",\n    auth=(\"username\", \"password\"),\n    preset=\"chrome-144\"\n)\n\n# Session-level auth\nsession = httpcloak.Session(\n    preset=\"chrome-144\",\n    auth=(\"username\", \"password\")\n)\n```\n\n### ⏰ Timeouts \u0026 Retries\n\n```python\n# Timeout\nsession = httpcloak.Session(preset=\"chrome-144\", timeout=30)\n\n# Per-request timeout\nr = session.get(\"https://slow-api.com/data\", timeout=60)\n```\n\n```go\n// Go: Timeout and retry configuration\nclient := client.NewClient(\"chrome-144\",\n    client.WithTimeout(30 * time.Second),\n    client.WithRetry(3),  // Retry 3 times on 429, 500, 502, 503, 504\n    client.WithRetryConfig(\n        5,                      // Max retries\n        500 * time.Millisecond, // Min backoff\n        10 * time.Second,       // Max backoff\n        []int{429, 503},        // Status codes to retry\n    ),\n)\n```\n\n### 🚫 Redirect Control\n\n```go\n// Disable automatic redirects\nclient := client.NewClient(\"chrome-144\",\n    client.WithoutRedirects(),\n)\n\nresp, _ := client.Get(ctx, \"https://example.com/redirect\", nil)\nfmt.Println(resp.StatusCode)              // 302\nfmt.Println(resp.GetHeader(\"location\"))   // Redirect URL\n```\n\n### 🔃 Refresh (Browser Page Refresh)\n\nSimulates a browser page refresh - closes all TCP/QUIC connections but keeps TLS session cache intact. On next request, connections use TLS resumption (like a real browser).\n\n```python\nsession = httpcloak.Session(preset=\"chrome-144\")\n\n# Make some requests\nsession.get(\"https://example.com/page1\")\nsession.get(\"https://example.com/page2\")\n\n# Simulate browser refresh (F5)\nsession.refresh()\n\n# Next request uses TLS resumption, looks like returning visitor\nsession.get(\"https://example.com/page1\")\n```\n\n**Go:**\n```go\nsession := httpcloak.NewSession(\"chrome-144\")\nsession.Get(ctx, \"https://example.com\")\nsession.Refresh()  // Close connections, keep TLS cache\nsession.Get(ctx, \"https://example.com\")  // TLS resumption\n```\n\n**Node.js:**\n```javascript\nsession.refresh();\n```\n\n**C#:**\n```csharp\nsession.Refresh();\n```\n\n### 🌐 Warmup (Browser Page Load)\n\nSimulates a real browser page load - fetches the HTML page and all its subresources (CSS, JS, images, fonts) with realistic headers, priorities, and timing. After warmup, the session has TLS session tickets, cookies, and cache headers populated.\n\n```python\nsession = httpcloak.Session(preset=\"chrome-144\")\n\n# Fetches page + subresources with realistic browser behavior\nsession.warmup(\"https://example.com\")\n\n# Subsequent requests look like follow-up navigation from a real user\nr = session.get(\"https://example.com/api/data\")\n```\n\n**Go:**\n```go\nsession := httpcloak.NewSession(\"chrome-144\")\nsession.Warmup(ctx, \"https://example.com\")\nsession.Get(ctx, \"https://example.com/api/data\")  // Looks like real user\n```\n\n**Node.js:**\n```javascript\nsession.warmup(\"https://example.com\");\n```\n\n**C#:**\n```csharp\nsession.Warmup(\"https://example.com\");\n```\n\n### 🔀 Fork (Parallel Browser Tabs)\n\nCreates N sessions that share cookies and TLS session caches with the parent but have independent connections. This simulates multiple browser tabs - same cookies, same TLS resumption tickets, same fingerprint, but independent TCP/QUIC connections for parallel requests.\n\n```python\nsession = httpcloak.Session(preset=\"chrome-144\")\nsession.warmup(\"https://example.com\")\n\n# Create 10 parallel \"tabs\" sharing cookies + TLS cache\ntabs = session.fork(10)\nfor i, tab in enumerate(tabs):\n    threading.Thread(\n        target=lambda t, n: t.get(f\"https://example.com/page/{n}\"),\n        args=(tab, i)\n    ).start()\n```\n\n**Go:**\n```go\nsession := httpcloak.NewSession(\"chrome-144\")\nsession.Warmup(ctx, \"https://example.com\")\n\ntabs := session.Fork(10)\nfor i, tab := range tabs {\n    go func(t *httpcloak.Session, n int) {\n        t.Get(ctx, fmt.Sprintf(\"https://example.com/page/%d\", n))\n    }(tab, i)\n}\n```\n\n**Node.js:**\n```javascript\nsession.warmup(\"https://example.com\");\nconst tabs = session.fork(10);\nawait Promise.all(tabs.map((tab, i) =\u003e tab.get(`https://example.com/page/${i}`)));\n```\n\n**C#:**\n```csharp\nsession.Warmup(\"https://example.com\");\nvar tabs = session.Fork(10);\nawait Task.WhenAll(tabs.Select((tab, i) =\u003e\n    Task.Run(() =\u003e tab.Get($\"https://example.com/page/{i}\"))\n));\n```\n\n### 🌍 Local Address Binding\n\nBind outgoing connections to a specific local IP address. Essential for IPv6 rotation scenarios where you have multiple IPs assigned to your machine.\n\n```python\n# Bind to specific IPv6 address\nsession = httpcloak.Session(\n    preset=\"chrome-144\",\n    local_address=\"2001:db8::1\"\n)\n\n# All requests use this source IP\nr = session.get(\"https://api.ipify.org\")\nprint(r.text)  # Shows 2001:db8::1\n\n# IPv4 works too\nsession = httpcloak.Session(\n    preset=\"chrome-144\",\n    local_address=\"192.168.1.100\"\n)\n```\n\n**Go:**\n```go\nsession := httpcloak.NewSession(\"chrome-144\",\n    httpcloak.WithLocalAddress(\"2001:db8::1\"),\n)\n```\n\n**Node.js:**\n```javascript\nconst session = new httpcloak.Session({\n    preset: \"chrome-144\",\n    localAddress: \"2001:db8::1\"\n});\n```\n\n**C#:**\n```csharp\nvar session = new Session(\n    preset: Presets.Chrome144,\n    localAddress: \"2001:db8::1\"\n);\n```\n\n**Note:** When a local address is set, target IPs are automatically filtered to match the address family (IPv6 local → only IPv6 targets).\n\n### 🔑 TLS Key Logging\n\nWrite TLS session keys to a file for traffic decryption in Wireshark. Works with HTTP/1.1, HTTP/2, and HTTP/3.\n\n```python\nsession = httpcloak.Session(\n    preset=\"chrome-144\",\n    key_log_file=\"/tmp/keys.log\"\n)\n\n# Make requests - keys written to file\nsession.get(\"https://example.com\")\n\n# In Wireshark: Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret log filename\n```\n\n**Go:**\n```go\nsession := httpcloak.NewSession(\"chrome-144\",\n    httpcloak.WithKeyLogFile(\"/tmp/keys.log\"),\n)\n```\n\n**Node.js:**\n```javascript\nconst session = new httpcloak.Session({\n    preset: \"chrome-144\",\n    keyLogFile: \"/tmp/keys.log\"\n});\n```\n\n**C#:**\n```csharp\nvar session = new Session(\n    preset: Presets.Chrome144,\n    keyLogFile: \"/tmp/keys.log\"\n);\n```\n\nAlso supports `SSLKEYLOGFILE` environment variable (standard NSS Key Log Format).\n\n---\n\n## API Reference\n\n### Python\n\n```python\nimport httpcloak\n\n# Module-level functions\nhttpcloak.get(url, **kwargs)\nhttpcloak.post(url, **kwargs)\nhttpcloak.put(url, **kwargs)\nhttpcloak.patch(url, **kwargs)\nhttpcloak.delete(url, **kwargs)\nhttpcloak.head(url, **kwargs)\nhttpcloak.options(url, **kwargs)\n\n# Session class\nsession = httpcloak.Session(\n    preset=\"chrome-144\",       # Browser preset (default)\n    proxy=\"socks5://...\",      # Proxy URL\n    timeout=30,                # Timeout in seconds\n    http_version=\"h3\",         # Force protocol: h1, h2, h3, auto\n    ech_from=\"cloudflare.com\", # ECH config source\n    auth=(\"user\", \"pass\"),     # Basic auth\n)\n\n# Session methods\nsession.get(url, **kwargs)\nsession.post(url, data=None, json=None, **kwargs)\nsession.get_stream(url)        # Streaming download\nsession.close()\n\n# Proxy switching\nsession.set_proxy(url)         # Set both TCP and UDP proxy\nsession.set_tcp_proxy(url)     # Set TCP proxy only (H1/H2)\nsession.set_udp_proxy(url)     # Set UDP proxy only (H3)\nsession.get_proxy()            # Get current proxy\nsession.get_tcp_proxy()        # Get current TCP proxy\nsession.get_udp_proxy()        # Get current UDP proxy\n\n# Header order customization\nsession.set_header_order(order)  # Set custom header order (list of lowercase names)\nsession.get_header_order()       # Get current header order\n\n# Session persistence (0-RTT resumption)\nsession.save(\"session.json\")   # Save to file\nsession = Session.load(\"session.json\")  # Load from file\ndata = session.marshal()       # Export as string\nsession = Session.unmarshal(data)  # Import from string\n\n# Response object\nresponse.status_code           # HTTP status\nresponse.ok                    # True if status \u003c 400\nresponse.text                  # Body as string\nresponse.content               # Body as bytes\nresponse.json()                # Parse JSON\nresponse.headers               # Response headers\nresponse.protocol              # h1, h2, or h3\nresponse.url                   # Final URL\nresponse.raise_for_status()    # Raise on 4xx/5xx\n```\n\n### Go\n\n```go\nimport \"github.com/sardanioss/httpcloak/client\"\n\n// Client creation\nc := client.NewClient(\"chrome-144\",\n    client.WithTimeout(30 * time.Second),\n    client.WithProxy(\"socks5://...\"),\n    client.WithRetry(3),\n    client.WithoutRedirects(),\n    client.WithInsecureSkipVerify(),\n)\ndefer c.Close()\n\n// Request methods\nresp, err := c.Get(ctx, url, headers)\nresp, err := c.Post(ctx, url, body, headers)\nresp, err := c.Put(ctx, url, body, headers)\nresp, err := c.Delete(ctx, url, headers)\n\n// Advanced request\nresp, err := c.Do(ctx, \u0026client.Request{\n    Method:        \"GET\",\n    URL:           url,\n    Headers:       map[string][]string{},\n    Body:          io.Reader,\n    Params:        map[string]string{},\n    ForceProtocol: client.ProtocolHTTP3,\n    FetchMode:     client.FetchModeCORS,\n    Referer:       \"https://example.com\",\n})\n\n// Proxy switching\nc.SetProxy(url)            // Set both TCP and UDP proxy\nc.SetTCPProxy(url)         // Set TCP proxy only (H1/H2)\nc.SetUDPProxy(url)         // Set UDP proxy only (H3)\nc.GetProxy()               // Get current proxy\nc.GetTCPProxy()            // Get current TCP proxy\nc.GetUDPProxy()            // Get current UDP proxy\n\n// Session persistence (0-RTT resumption)\nc.Save(\"session.json\")     // Save to file\nc, _ = client.Load(\"session.json\")  // Load from file\ndata, _ := c.Marshal()     // Export as string\nc, _ = client.Unmarshal(data)  // Import from string\n\n// Response object\nresp.StatusCode\nresp.Protocol\nresp.Headers\nresp.Body           // io.ReadCloser\nresp.Text()         // (string, error)\nresp.Bytes()        // ([]byte, error)\nresp.JSON(\u0026v)       // error\nresp.GetHeader(key) // string\nresp.IsSuccess()    // bool\nresp.IsRedirect()   // bool\n```\n\n### Node.js\n\n```javascript\nimport httpcloak from \"httpcloak\";\n\n// Session creation\nconst session = new httpcloak.Session({\n    preset: \"chrome-144\",\n    proxy: \"socks5://...\",\n    timeout: 30000,\n    httpVersion: \"h3\",\n});\n\n// Async methods\nawait session.get(url, options)\nawait session.post(url, { json, data, headers })\nawait session.put(url, options)\nawait session.delete(url, options)\n\n// Sync methods\nsession.getSync(url, options)\nsession.postSync(url, options)\nsession.close()\n\n// Proxy switching\nsession.setProxy(url)          // Set both TCP and UDP proxy\nsession.setTcpProxy(url)       // Set TCP proxy only (H1/H2)\nsession.setUdpProxy(url)       // Set UDP proxy only (H3)\nsession.getProxy()             // Get current proxy\nsession.getTcpProxy()          // Get current TCP proxy\nsession.getUdpProxy()          // Get current UDP proxy\nsession.proxy                  // Property accessor (get/set)\n\n// Session persistence (0-RTT resumption)\nsession.save(\"session.json\")   // Save to file\nsession = httpcloak.Session.load(\"session.json\")  // Load from file\nconst data = session.marshal() // Export as string\nsession = httpcloak.Session.unmarshal(data)  // Import from string\n\n// Response object\nresponse.statusCode\nresponse.ok\nresponse.text\nresponse.json()\nresponse.headers\nresponse.protocol\n```\n\n### C#\n\n```csharp\nusing HttpCloak;\n\n// Session creation\nvar session = new Session(\n    preset: Presets.Chrome144,\n    proxy: \"socks5://...\",\n    timeout: 30\n);\n\n// Request methods\nsession.Get(url, headers)\nsession.Post(url, body, headers)\nsession.PostJson\u003cT\u003e(url, data, headers)\nsession.Put(url, body, headers)\nsession.Delete(url)\nsession.Dispose()\n\n// Proxy switching\nsession.SetProxy(url)          // Set both TCP and UDP proxy\nsession.SetTcpProxy(url)       // Set TCP proxy only (H1/H2)\nsession.SetUdpProxy(url)       // Set UDP proxy only (H3)\nsession.GetProxy()             // Get current proxy\nsession.GetTcpProxy()          // Get current TCP proxy\nsession.GetUdpProxy()          // Get current UDP proxy\nsession.Proxy                  // Property accessor (get/set)\n\n// Session persistence (0-RTT resumption)\nsession.Save(\"session.json\")   // Save to file\nvar session = Session.Load(\"session.json\")  // Load from file\nvar data = session.Marshal()   // Export as string\nvar session = Session.Unmarshal(data)  // Import from string\n\n// Response object\nresponse.StatusCode\nresponse.Ok\nresponse.Text\nresponse.Json\u003cT\u003e()\nresponse.Headers\nresponse.Protocol\n```\n\n---\n\n## Browser Presets\n\n| Preset | Platform | PQ | H3 |\n|--------|----------|:--:|:--:|\n| `chrome-144` | Auto | ✅ | ✅ |\n| `chrome-144-windows` | Windows | ✅ | ✅ |\n| `chrome-144-macos` | macOS | ✅ | ✅ |\n| `chrome-144-linux` | Linux | ✅ | ✅ |\n| `chrome-143` | Auto | ✅ | ✅ |\n| `chrome-143-windows` | Windows | ✅ | ✅ |\n| `chrome-143-macos` | macOS | ✅ | ✅ |\n| `chrome-143-linux` | Linux | ✅ | ✅ |\n| `firefox-133` | Auto | ❌ | ❌ |\n| `safari-18` | macOS | ❌ | ✅ |\n| `ios-safari-18` | iOS | ❌ | ✅ |\n| `android-chrome-144` | Android | ✅ | ✅ |\n| `android-chrome-143` | Android | ✅ | ✅ |\n| `ios-chrome-144` | iOS | ✅ | ✅ |\n| `ios-chrome-143` | iOS | ✅ | ✅ |\n\n**PQ** = Post-Quantum (X25519MLKEM768) · **H3** = HTTP/3\n\n---\n\n## Testing Tools\n\n| Tool | Tests |\n|------|-------|\n| [tls.peet.ws](https://tls.peet.ws/api/all) | JA3, JA4, HTTP/2 Akamai |\n| [quic.browserleaks.com](https://quic.browserleaks.com/) | HTTP/3 QUIC fingerprint |\n| [cf.erisa.uk](https://cf.erisa.uk/) | Cloudflare bot score |\n| [cloudflare.com/cdn-cgi/trace](https://www.cloudflare.com/cdn-cgi/trace) | ECH status, TLS version |\n\n---\n\n## Dependencies\n\nCustom forks for browser-accurate fingerprinting:\n\n- [sardanioss/utls](https://github.com/sardanioss/utls) — TLS fingerprinting\n- [sardanioss/quic-go](https://github.com/sardanioss/quic-go) — HTTP/3 fingerprinting\n- [sardanioss/net](https://github.com/sardanioss/net) — HTTP/2 frame fingerprinting\n\n---\n\n## Connect\n\n- Discord: **sardanioss**\n- Email: **sakshamsolanki126@gmail.com**\n\n---\n\n\u003cp align=\"center\"\u003e\nMIT License\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsardanioss%2Fhttpcloak","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsardanioss%2Fhttpcloak","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsardanioss%2Fhttpcloak/lists"}