# chaser-cf
[](https://crates.io/crates/chaser-cf)
[](https://docs.rs/chaser-cf)
[](LICENSE-MIT)
Cloudflare bypass library powered by stealth browser automation. No captcha API tokens needed — pure browser-based challenge solving with C FFI bindings for use from any language.
## How it works
- Launches a real Chrome instance with a native fingerprint (OS, RAM, UA, Client Hints all consistent)
- For WAF/managed challenges: polls for `cf_clearance` and clicks the Turnstile checkbox via CDP shadow-root traversal — Cloudflare's widget lives inside a closed shadow root that JS can't reach, but CDP can
- For Turnstile tokens: injects an extractor script and optionally intercepts the page request to serve a minimal HTML stub, reducing load time and noise
- Built on [chaser-oxide](https://github.com/ccheshirecat/chaser-oxide), a stealth fork of chromiumoxide
## Features
- **WAF Session** — extracts `cf_clearance` + `user-agent` for use in subsequent HTTP requests
- **Turnstile max** — solves Turnstile with full page load (no site key needed)
- **Turnstile min** — solves Turnstile with request interception, much faster (site key required)
- **Page source** — returns HTML after challenge is cleared
- **Proxy support** — per-request proxy with optional auth
- **C FFI** — use from Python, Go, Node.js, C/C++, etc.
- **HTTP server** — optional REST API (feature-flagged)
## Installation
```toml
[dependencies]
chaser-cf = { version = "0.1.6" }
```
Requires Chrome or Chromium installed on the system.
## Usage
### Rust
```rust
use chaser_cf::{ChaserCF, ChaserConfig};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let chaser = ChaserCF::new(ChaserConfig::default()).await?;
// WAF session — returns cookies + user-agent for use in reqwest/ureq/etc.
let session = chaser.solve_waf_session("https://example.com", None).await?;
println!("cf_clearance: {}", session.cookies_string());
println!("user-agent: {}", session.headers["user-agent"]);
// Page source after challenge cleared
let html = chaser.get_source("https://example.com", None).await?;
// Turnstile token (full page, no site key needed)
let token = chaser.solve_turnstile("https://example.com", None).await?;
// Turnstile token (fast, request interception — needs site key)
let token = chaser.solve_turnstile_min(
"https://example.com",
"0x4AAAAAAxxxxx",
None,
).await?;
chaser.shutdown().await;
Ok(())
}
```
With proxy:
```rust
use chaser_cf::ProxyConfig;
let proxy = ProxyConfig::new("1.2.3.4", 8080)
.with_auth("user", "pass");
let session = chaser.solve_waf_session("https://example.com", Some(proxy)).await?;
```
### Configuration
```rust
let config = ChaserConfig::default()
.with_context_limit(10) // max concurrent browser contexts
.with_timeout_ms(60_000) // per-operation timeout
.with_lazy_init(true) // don't launch browser until first use
.with_headless(true) // headless mode
.with_chrome_path("/usr/bin/chromium");
```
| `context_limit` | 20 | Max concurrent browser contexts |
| `timeout_ms` | 60000 | Per-operation timeout (ms) |
| `lazy_init` | false | Defer browser launch until first use |
| `headless` | false | Run browser headless |
| `chrome_path` | auto | Path to Chrome/Chromium binary |
### Testing
```bash
cargo run --example test_turnstile -- <url> [options]
Options:
--headless Run headless
--proxy <host:port> Proxy address
--proxy-auth <user:pass> Proxy credentials
--site-key <key> Turnstile site key (enables min mode)
--timeout <ms> Timeout in ms (default: 120000)
# Examples
cargo run --example test_turnstile -- https://stake.com
cargo run --example test_turnstile -- https://stake.com --headless --mode waf
cargo run --example test_turnstile -- https://example.com --site-key 0x4AA... --mode min
cargo run --example test_turnstile -- https://stake.com --proxy 1.2.3.4:8080 --proxy-auth user:pass
```
## HTTP Server
```bash
cargo run --release --features http-server --bin chaser-cf-server
```
```bash
# WAF session
curl -X POST http://localhost:3000/solve \
-H "Content-Type: application/json" \
-d '{"mode": "waf-session", "url": "https://example.com"}'
# Page source
curl -X POST http://localhost:3000/solve \
-H "Content-Type: application/json" \
-d '{"mode": "source", "url": "https://example.com"}'
# Turnstile (full page)
curl -X POST http://localhost:3000/solve \
-H "Content-Type: application/json" \
-d '{"mode": "turnstile-max", "url": "https://example.com"}'
# Turnstile (minimal)
curl -X POST http://localhost:3000/solve \
-H "Content-Type: application/json" \
-d '{"mode": "turnstile-min", "url": "https://example.com", "siteKey": "0x4AAA..."}'
# With proxy
curl -X POST http://localhost:3000/solve \
-H "Content-Type: application/json" \
-d '{"mode": "waf-session", "url": "https://example.com", "proxy": {"host": "1.2.3.4", "port": 8080}}'
```
### Environment variables
| `PORT` | `3000` | HTTP server port |
| `CHASER_CONTEXT_LIMIT` | `20` | Max concurrent contexts |
| `CHASER_TIMEOUT` | `60000` | Timeout (ms) |
| `CHASER_LAZY_INIT` | `false` | Lazy browser init |
| `CHASER_HEADLESS` | `false` | Headless mode |
| `CHROME_BIN` | auto | Chrome binary path |
| `AUTH_TOKEN` | none | Optional API auth token |
## C FFI
Build the shared library:
```bash
cargo build --release
# headers written to include/chaser_cf.h
```
```c
#include "chaser_cf.h"
void on_result(const char* json, void* ctx) {
printf("%s\n", json);
chaser_free_string((char*)json);
}
int main() {
ChaserConfig cfg = chaser_config_default();
chaser_init(&cfg);
chaser_solve_waf_async("https://example.com", NULL, NULL, on_result);
sleep(30);
chaser_shutdown();
}
```
```bash
gcc example.c -L./target/release -lchaser_cf -lpthread -ldl -lm -o example
```
### Python (ctypes)
```python
import ctypes, json, time
from ctypes import c_char_p, c_void_p, CFUNCTYPE
lib = ctypes.CDLL('./target/release/libchaser_cf.so')
CALLBACK = CFUNCTYPE(None, c_char_p, c_void_p)
result = []
@CALLBACK
def on_result(json_bytes, _):
result.append(json.loads(json_bytes))
lib.chaser_free_string(json_bytes)
lib.chaser_init(None)
lib.chaser_solve_waf_async(b"https://example.com", None, None, on_result)
while not result:
time.sleep(0.1)
print(result[0])
lib.chaser_shutdown()
```
## Response format
```json
{ "type": "WafSession", "data": { "cookies": [{"name": "cf_clearance", "value": "..."}], "headers": {"user-agent": "..."} } }
{ "type": "Token", "data": "0.abc123..." }
{ "type": "Source", "data": "<html>..." }
{ "type": "Error", "data": {"code": 6, "message": "timed out"} }
```
## License
MIT OR Apache-2.0