wreq: a Rust HTTP client with fine-grained TLS and HTTP/2 control
An ergonomic, privacy-aware Rust HTTP Client
At a glance
- What is it?
- wreq is a Rust HTTP client built for high-fidelity protocol matching, giving you control over TLS extensions, HTTP/2 settings and header case rather than a fixed fingerprint string, with support for HTTP/3, WebSockets and several async runtimes. It suits a developer whose requests are being rejected by a WAF and who needs to fix the request rather than work around the block.
- Who is it for?
- Adopt wreq if your Rust application is being rejected by a WAF or needs to match a specific browser's protocol behaviour and you can control the TLS and HTTP/2 settings yourself. Skip it if a standard client would do, since the extra fidelity is work you take on deliberately.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the crate is, and the problem it addresses
wreq is an ergonomic and modular Rust HTTP Client for high-fidelity protocol matching, featuring customizable TLS, JA3, JA4 and HTTP/2 signature capabilities. The package description calls it an ergonomic, privacy-aware Rust HTTP Client, and the topics list names the surface it covers, with tls, tls-fingerprint, ja3, ja4, akamai, http2, http3, quic, websocket, crawler, scraper and fingerprint. The primary language is Rust, the licence is Apache-2.0, the homepage is wreq.org, the last push was on 2026-10-15, and the releases are v0.16.1 on 2026-08-27, v0.16.0 on 2026-08-22 and a v6.0.0-rc.31 release candidate from 2026-08-18. The design question the crate answers is simple to state and hard to solve with an ordinary client. Some servers look at more than the URL and the headers, and if the request does not look like it came from the client you claim to be, it is refused.
Header case, and why it matters more than it sounds
The first behaviour section is about HTTP/1 over TLS. In the Rust ecosystem, most HTTP clients rely on the http library, which performs well but does not preserve header case, and this causes some WAFs to reject HTTP/1 requests with lowercase headers. wreq addresses this by fully supporting HTTP/1 header case sensitivity. This is a small implementation detail with a large effect, because a WAF rule that matches on a header name in the conventional capitalisation will not fire against a lowercased one, so the request either behaves differently than the site author intended or is rejected outright. The crate also keeps the original header, listed among the features, so a request can carry the exact header ordering and casing the target expects rather than what a convenience client produces.
Why fingerprint strings are not enough, and what replaces them
The second behaviour section covers HTTP/2 over TLS, and the reasoning is the core of the project. Due to the complexity of TLS encryption and the widespread adoption of HTTP/2, browser fingerprints such as JA3, JA4 and Akamai cannot be reliably emulated using simple fingerprint strings. Instead of parsing and emulating these string-based fingerprints, wreq provides fine-grained control over TLS and HTTP/2 extensions and settings for precise browser behaviour emulation. That is a substantial difference in approach. A fingerprint string is a hash of a few observable parameters, and reproducing the hash tells the server what you claim to be, while setting the actual extension list, the ciphers, the ALPN values and the HTTP/2 settings and frame ordering makes the claim true. The third behaviour section adds the device level, with more than 100 browser device emulation profiles maintained in the separate wreq-util repository, because TLS and HTTP/2 fingerprints are often identical across browser models as those protocols evolve slower than browser release cycles.
The feature set, and how it is put together
The feature list is a set of independent pieces rather than a single idea. Bodies come as plain, JSON, urlencoded and multipart. There is HTTP Trailer support, a cookie store, a redirect policy, the original header, and rotating proxies. Tower Middleware is there, so the client composes with the middleware ecosystem. WebSocket upgrade is supported, HTTPS goes through BoringSSL, there is HTTP/2 over TLS parity, a certificate store covering CAs and mTLS, and multiple runtime support across Tokio and Compio. The modularity is the point. A client that hardcodes one fingerprint forces you to fork it to change anything, while a client with fine-grained settings and a separate profiles library lets you compose the behaviour you need from parts that are each independently maintained.
The example, and how the pieces fit together
The README example shows the shape of the API. The dependencies are the runtime with optional features enabled, plus the client and the utility crate:
[dependencies]
tokio = { version = "1", features = ["full"] }
wreq = "0.16"
wreq-util = "0.2"and the code builds a client with an emulation profile and uses the API as you would expect:
use wreq::Client;
use wreq_util::Emulation;
#[tokio::main]
async fn main() -> wreq::Result<()> {
// Build a client
let client = Client::builder()
.emulation(Emulation::Safari26)
.build()?;
// Use the API you're already familiar with
let resp = client.get("https://pingly.us.kg/api/all").send().await?;
println!("{}", resp.text().await?);
Ok(())
}The point of the example is that the emulation is a builder option and everything else is the client interface you already know. The `Emulation::Safari26` variant names a browser and a version, and the crate does the work of setting the TLS and HTTP/2 parameters to match.
The feature flags, and what they pull in
The Cargo manifest makes the dependency structure unusually clear, and it explains the crate's size. The default features are `webpki-roots` and `tokio-rt`, so a default build has the webpki root store and the Tokio runtime and nothing else. The runtime choice is a feature, with `compio-rt` enabling the completion-based runtime using io_uring and IOCP, and `tokio-rt` enabling Tokio. The body formats are opt-in: `charset` for decoding text, `cookies` for the cookie store, `gzip`, `brotli`, `zstd` and `deflate` for decompression, each pulling in tower-http, `query` and `form` for URL query string and x-www-form-urlencoded support through serde, `json` for JSON, and `multipart` for multipart form data. The TLS side is where the weight sits, since HTTPS goes through BoringSSL. The `docs.rs` metadata sets `all-features = true` and targets `x86_64-unknown-linux-gnu`, which is a hint about where the full feature set is exercised.
Building it, and the openssl conflict
Building from source needs the BoringSSL build dependencies, and the README gives the command:
sudo apt-get install build-essential cmake perl pkg-config libclang-dev musl-tools git -y
cargo build --releaseThere is one specific trap in the build that the README calls out. Compiling alongside openssl-sys can cause symbol conflicts with BoringSSL that lead to link failures, and on Linux and Android this can be avoided by enabling the `prefix-symbols` feature. A GitHub Actions workflow is provided for compiling the project on Linux, Windows and macOS. The `examples/` directory in the repository is a good index of what the client can do, with files for the certificate store, WebSocket upgrades over both HTTP/1 and HTTP/2, JSON in dynamic and typed forms, form posts, key logging, emulation, proxies, redirects, version selection, local addresses, Unix sockets, and a lower priority Tokio runtime, which together cover most of the surface the feature list advertises.
Editorial conclusion
Adopt wreq if your Rust application is being rejected by a WAF or needs to match a specific browser's protocol behaviour and you can control the TLS and HTTP/2 settings yourself. Skip it if a standard client would do, since the extra fidelity is work you take on deliberately. Before you start, note the runtime floor, where the manifest requires Rust 1.98 on edition 2024, and the build complication, where compiling alongside openssl-sys can cause symbol conflicts with BoringSSL that the `prefix-symbols` feature avoids on Linux and Android.
Frequently asked questions
What is wreq?
An ergonomic, privacy-aware Rust HTTP client for high-fidelity protocol matching, with customizable TLS, JA3 and JA4 and HTTP/2 signature capabilities. It supports plain, JSON, urlencoded and multipart bodies, HTTP trailers, a cookie store, redirect policy, rotating proxies, Tower middleware, WebSocket upgrade and multiple async runtimes.
Why does wreq care about HTTP/1 header case?
Because most Rust HTTP clients rely on the http library, which performs well but does not preserve header case, and this causes some WAFs to reject HTTP/1 requests with lowercase headers. wreq fully supports HTTP/1 header case sensitivity.
How does wreq handle browser fingerprints like JA3 and JA4?
It does not parse and emulate the string-based fingerprints, because the complexity of TLS encryption and the widespread adoption of HTTP/2 make that unreliable. Instead it provides fine-grained control over TLS and HTTP/2 extensions and settings for precise browser behaviour emulation.
How do I build wreq, and what is the openssl conflict?
Install the BoringSSL build dependencies and run `cargo build --release`. Compiling alongside openssl-sys can cause symbol conflicts with BoringSSL that lead to link failures, which on Linux and Android can be avoided by enabling the prefix-symbols feature.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/0x676e67-wreq)Was this useful?