custls - A minimal-invasive fork of rustls with browser-level TLS fingerprint simulation
Based on rustls - A modern TLS library written in Rust
custls is a specialized fork of rustls that adds browser-level TLS ClientHello fingerprint simulation capabilities while maintaining all of rustls's security guarantees and performance characteristics.
- π Browser Fingerprint Simulation - Accurately simulate Chrome, Firefox, Safari, and Edge TLS fingerprints
- π Smart Randomization - Apply natural variations to avoid static fingerprint detection
- πΎ Fingerprint Caching - Automatically cache successful fingerprints for consistent behavior
- π£ Multi-Phase Hooks - Fine-grained control over ClientHello construction (4 hook phases)
- β‘ Minimal Overhead - Only 3.9-5.9% performance impact (well under 10% target)
- π Security Preserved - Zero unsafe code, all rustls security guarantees maintained
- π§ Minimal Invasiveness - <100 lines of modifications to rustls core
- π Easy Integration - Works seamlessly with hyper, reqwest, and other HTTP clients
- Web Scraping - Bypass TLS fingerprint-based bot detection (Cloudflare, Akamai, DataDome)
- API Testing - Test fingerprint detection systems with realistic browser traffic
- Security Research - Analyze and understand TLS fingerprinting techniques
- Privacy Tools - Build tools that resist fingerprinting-based tracking
Add to your Cargo.toml:
[dependencies] rustls = { path = "./custls/rustls" } # Use custls fork hyper = { version = "0.14", features = ["client", "http1", "http2"] } hyper-rustls = "0.24" tokio = { version = "1", features = ["full"] }use std::sync::Arc; use rustls::custls::{CustlsConfig, BrowserTemplate, RandomizationLevel, DefaultCustomizer}; // Configure custls to simulate Chrome 130 let custls_config = CustlsConfig::builder() .with_template(BrowserTemplate::Chrome130) .with_randomization_level(RandomizationLevel::Light) .with_cache(true) .build(); let customizer = Arc::new(DefaultCustomizer::new(custls_config)); // Use with your HTTP client (hyper, reqwest, etc.) // See examples/ directory for complete integration examplesuse hyper::{Client, Request, Body}; use hyper_rustls::HttpsConnectorBuilder; use rustls::ClientConfig; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Configure rustls with custls let mut tls_config = ClientConfig::builder() .with_safe_defaults() .with_native_roots() .with_no_client_auth(); // Build HTTPS connector let https = HttpsConnectorBuilder::new() .with_tls_config(tls_config) .https_only() .enable_http2() .build(); // Create hyper client let client = Client::builder().build::<_, Body>(https); // Make requests with browser-like TLS fingerprints let req = Request::builder() .uri("https://example.com") .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") .body(Body::empty())?; let res = client.request(req).await?; println!("Status: {}", res.status()); Ok(()) }- π Quick Start Guide (δΈζ) - 5-minute introduction
- π Integration Guide - Complete integration documentation
- π Design Document - Architecture and design decisions
- π Requirements Document - Detailed requirements
- π» Examples - Working code examples
custls_basic_usage.rs- Basic configuration patternscustls_http_client.rs- HTTP client integration patternshyper_custls_complete.rs- Complete hyper integrationcustls_custom_hooks.rs- Custom hook implementationcustls_custom_template.rs- Custom template creationcustls_zero_overhead.rs- Zero-overhead mode
custls includes accurate templates for major browsers:
| Template | Description | Use Case |
|---|---|---|
Chrome130 | Chrome 130+ (Chromium-based) | Most common, best compatibility |
Firefox135 | Firefox 135+ (Gecko-based) | Unique fingerprint, good diversity |
Safari17 | Safari 17+ (WebKit-based) | macOS/iOS scenarios |
Edge130 | Edge 130+ (Chromium-based) | Windows-specific scenarios |
Custom | User-defined template | Advanced customization |
Control the degree of fingerprint variation:
| Level | Overhead | Description | Use Case |
|---|---|---|---|
None | ~3.9% | Exact template match | Maximum performance |
Light | ~5.1% | Small browser-style variations | Recommended - Natural behavior |
Medium | ~4.7% | Moderate variations | Moderate anti-fingerprinting |
High | ~4.7% | Maximum variation | Strong anti-fingerprinting |
Based on comprehensive benchmarks:
| Configuration | Latency | Overhead |
|---|---|---|
| Vanilla rustls | 28.5ΞΌs | Baseline |
| custls (None) | 29.6ΞΌs | +3.9% |
| custls (Light) | 30.0ΞΌs | +5.1% β |
| custls (Medium) | 29.8ΞΌs | +4.7% |
| custls (High) | 29.8ΞΌs | +4.7% |
Cache Performance:
- Cache lookup: <1ns (effectively instant)
- Hook invocation: <1ns (zero overhead)
β All performance targets met - Well under 10% overhead requirement!
custls uses a minimal-invasive architecture:
βββββββββββββββββββββββββββββββββββββββββββ β Application Layer β β (HTTP clients: hyper, reqwest, etc.) β ββββββββββββββββββββ¬βββββββββββββββββββββββ β ββββββββββββββββββββΌβββββββββββββββββββββββ β custls Public API β β - CustlsConfig β β - ClientHelloCustomizer trait β β - BrowserTemplate enum β β - RandomizationLevel enum β ββββββββββββββββββββ¬βββββββββββββββββββββββ β ββββββββββββββββββββΌβββββββββββββββββββββββ β custls Core Modules β β (All in src/custls/ - isolated) β β - hooks.rs - templates.rs β β - randomizer.rs - state.rs β β - extensions.rs - utils.rs β ββββββββββββββββββββ¬βββββββββββββββββββββββ β ββββββββββββββββββββΌβββββββββββββββββββββββ β rustls Core (Minimally Modified) β β - <100 lines of modifications β β - Strategic hook insertion points β β - Field exposure for customization β ββββββββββββββββββββββββββββββββββββββββββββ custls makes minimal, surgical modifications to rustls:
- Total modifications: <100 lines across 5 files
- Files modified:
src/lib.rs: 1 line (module declaration)src/msgs/client_hello.rs: ~30 lines (field exposure)src/msgs/enums.rs: ~20 lines (extension types)- Integration points: minimal hook insertions
All custls logic is isolated in src/custls/ for easy maintenance and upstream rebasing.
let config = CustlsConfig::builder() .with_template(BrowserTemplate::Chrome130) .with_randomization_level(RandomizationLevel::Light) .with_cache(true) .build();let config = CustlsConfig::builder() .with_template(BrowserTemplate::Firefox135) .with_randomization_level(RandomizationLevel::Medium) .with_cache(true) .with_max_cache_size(1000) .build();use rustls::custls::ClientHelloCustomizer; #[derive(Debug)] struct MyCustomHooks; impl ClientHelloCustomizer for MyCustomHooks { // Implement custom hook methods for fine-grained control // 4 phases: config, components, structure, wire bytes } let customizer = Arc::new(MyCustomHooks);custls includes comprehensive testing:
- β 230+ unit tests - All core functionality validated
- β 17 property-based tests - Universal correctness properties verified
- β Integration tests - Complete handshake flows validated
- β Browser validation tests - Template fidelity confirmed
- β Performance benchmarks - Overhead monitoring
Run tests:
# Run all tests cargo test --lib custls # Run benchmarks cargo bench --bench custls_benchmarks # Build examples cargo build --examplescustls maintains all rustls security guarantees:
- β Zero unsafe code in custls module
- β RFC 8446 downgrade protection implemented
- β Certificate validation fully preserved
- β Constant-time operations maintained
- β Session security properly handled
custls works seamlessly with popular Rust HTTP clients:
let https = HttpsConnectorBuilder::new() .with_tls_config(tls_config) .https_only() .enable_http2() .build(); let client = Client::builder().build::<_, Body>(https);let client = Client::builder() .use_preconfigured_tls(tls_config) .build()?;See Integration Guide for complete examples.
-
Match HTTP headers to TLS fingerprint
// TLS: Chrome 130 β HTTP headers: Chrome 130 .header("User-Agent", "Mozilla/5.0 ... Chrome/130.0.0.0 ...")
-
Enable caching for consistency
.with_cache(true) // Maintain consistent fingerprints per target
-
Use Light randomization
.with_randomization_level(RandomizationLevel::Light) // Natural variation
-
Reuse clients
let client = create_client(config); for url in urls { client.get(url).await?; }
- Don't mix fingerprints and headers - TLS Chrome + HTTP Firefox = detected
- Don't over-randomize - High level may produce unnatural fingerprints
- Don't frequently switch templates - Appears as abnormal behavior
- Don't ignore errors - Connection failures may indicate detection
Cause: Fingerprint detected as abnormal
Solution:
// Try different template .with_template(BrowserTemplate::Firefox135) // Adjust randomization .with_randomization_level(RandomizationLevel::Medium) // Clear cache .with_cache(false)Cause: High randomization or cache disabled
Solution:
// Lower randomization .with_randomization_level(RandomizationLevel::Light) // Enable cache .with_cache(true)- Core browser templates (Chrome, Firefox, Safari, Edge)
- Multi-phase hook system
- Fingerprint caching
- Comprehensive testing
- Performance optimization
- ECH (Encrypted Client Hello) support
- Post-quantum cryptography hooks
- QUIC ClientHello customization
- Additional browser templates
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
custls is distributed under the same licenses as rustls:
- Apache License version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
- ISC license (LICENSE-ISC)
You may use this software under the terms of any of these licenses, at your option.
custls is built on top of rustls, an excellent TLS library created and maintained by:
- Joe Birr-Pixton (@ctz, Project Founder)
- Dirkjan Ochtman (@djc, Co-maintainer)
- Daniel McCarney (@cpu, Co-maintainer)
- Josh Aas (@bdaehlie, Project Management)
Special thanks to the rustls team for creating such a solid foundation.
- π Documentation
- π» Examples
- π Issue Tracker
- π¬ Discussions
custls is designed for legitimate use cases such as web scraping, testing, and security research. Users are responsible for ensuring their use complies with applicable laws and terms of service.
custls - Browser-level TLS fingerprint simulation for Rust
Built with β€οΈ on top of rustls
