stealthreq 0.2.0

Trait-driven, human-like request mutation primitives for crawlers and scrapers.
Documentation
stealthreq-0.2.0 has been yanked.

stealthreq

Generate human-like request behavior for scraping and security scanning. Applies realistic headers, timing jitter, and TLS fingerprint rotation to make automated requests resemble genuine browser traffic.

use stealthreq::{StealthPolicy, MutableRequest, AppliedRequestProfile};

// Implement MutableRequest for your HTTP client
struct MyRequest {
    headers: Vec<(String, String)>,
}

impl MutableRequest for MyRequest {
    fn set_header(&mut self, name: &str, value: &str) {
        self.headers.push((name.to_string(), value.to_string()));
    }
}

fn main() -> stealthreq::Result<()> {
    let mut req = MyRequest::default();
    let policy = StealthPolicy::default();
    
    let applied: AppliedRequestProfile = policy.apply(&mut req)?;
    
    println!("User-Agent: {}", applied.user_agent);
    println!("Jitter: {:?}", applied.jitter);
    println!("TLS profile: {}", applied.tls_profile.name);
    
    Ok(())
}

Why this exists

Scrapers and scanners get blocked by WAFs that look for automation signals. Missing Accept-Language headers. Static User-Agent strings. Perfectly consistent timing. Predictable TLS fingerprints.

stealthreq decouples the mutation logic from HTTP implementations. You implement a 1-method trait for your request type. The crate handles header selection, timing, and TLS profile rotation. Works with reqwest, hyper, ureq, or any other HTTP client.

MutableRequest trait

Libraries integrate by implementing MutableRequest:

pub trait MutableRequest {
    fn set_header(&mut self, name: &str, value: &str);
    fn set_user_agent(&mut self, value: &str) {
        self.set_header("User-Agent", value);
    }
}

This minimal interface keeps the crate HTTP-agnostic.

Header policies

HeaderPolicy manages pools of realistic browser values:

  • 4 desktop and mobile User-Agent strings
  • Accept-Language variants (en-US, en-GB, en-CA, fr-FR)
  • Accept-Encoding preferences (gzip, deflate, br)
  • Cache-Control directives
  • Referer hosts
  • Optional Pragma headers

The policy randomly selects headers up to a configurable budget, always preserving User-Agent.

Timing jitter

TimingJitter generates random delays between requests:

use stealthreq::{StealthPolicy, TimingJitter};

let policy = StealthPolicy::default()
    .with_timing(TimingJitter::new(100, 500)); // 100-500ms delay

The AppliedRequestProfile includes the sampled delay. Your code sleeps before sending.

TLS fingerprint rotation

TlsRotationPolicy provides browser-like JA3 fingerprints:

Profile Description
chrome-desktop-121 Chrome 121 on desktop
chrome-mobile-121 Chrome 121 on mobile
firefox-desktop Firefox desktop

Each profile includes JA3 digest, ALPN order, cipher suites, and extension ordering. Bind these to your TLS stack configuration.

Configuration via TOML

use stealthreq::StealthProfileConfig;

let config = StealthProfileConfig::from_toml(r#"
jitter_ms_min = 100
jitter_ms_max = 500
header_budget = 6
rotate_tls = true
seed = 42

[headers]
referer_hosts = ["https://google.com", "https://twitter.com"]
include_pragmas = true
"#)?;

let policy = config.build();

Deterministic profiles

Set a seed for reproducible behavior in tests:

let policy = StealthPolicy::default().with_seed(Some(12345));

// Same seed produces same headers, timing, and TLS profile
let applied1 = policy.apply(&mut req1)?;
let applied2 = policy.apply(&mut req2)?;
assert_eq!(applied1.user_agent, applied2.user_agent);

Contributing

Pull requests are welcome. There is no such thing as a perfect crate. If you find a bug, a better API, or just a rough edge, open a PR. We review quickly.

License

MIT. Copyright 2026 CORUM COLLECTIVE LLC.

crates.io docs.rs