All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog and this project follows Semantic Versioning.
Unreleased
[0.6.0] - 28.08.2026
Fixed
- Impersonated requests sent
user-agent: req/<version>. The profile user-agent did not go on the wire. Req'sput_user_agentstep set the header, and a per-request header wins over the client defaults. Thusreq/<version>went out next to the profilesec-ch-ua. A detector can find this pair.attach/2andimpersonate/2now remove that default, and the profile sets the user-agent. An explicituser-agentheader or:user_agentoption still wins. - Impersonated requests sent
accept-encoding: zstd,gzip,deflate,br. This was not the profile value. Thewreq-utilbuild did not use theemulation-compressionfeature, so wreq set its own value and order. The feature is now on. Chrome now sendsgzip, deflate, br, zstd, the same as the real browser, and the header is in the browser position. Responses still decode.
Changed
- Require Req 0.7 (
~> 0.7, was~> 0.5 or ~> 0.6). Req 0.7 makes function adapters obsolete.attach/2andimpersonate/2now setadapter: CloakedReq. Change a check ofis_function(req.adapter, 1)toreq.adapter == CloakedReq. Req 0.5 and Req 0.6 cannot call a module adapter, thus they are not supported. - Move the Rust NIF to the stable
wreqline:wreq6.0.0-rc.29 to 0.16.1, andwreq-util3.0.0-rc.14 to 0.2.0. Upstream reset the version numbers on 2026-08-22. It is the same project with new numbers, but the jump also includes rc.30, rc.31 and the two 0.16 releases. The bullets below give the changes that you can see. - Impersonation headers are different for each profile. There are still 133 profiles, and no profile was added or removed. The header set and the header order changed. Chrome now also sends
upgrade-insecure-requests: 1andsec-fetch-user: ?1, and itssec-fetch-*block is now afteraccept. Firefox carrieste: trailerson the profile. Opera has thesec-fetch-*group, and itsacceptchangessigned-exchange;v=b3fromq=0.9toq=0.7. Test again each profile that you tuned for a detector. - Cookies: a host-only cookie no longer goes to subdomains. A host-only cookie has no
Domainattribute. The old jar matched the suffix, thus a cookie forexample.comalso went tosub.example.com. A session that needs this now fails, and there is no error message. - Cookies:
Max-Agenow expires a cookie. The old jar read onlyExpires, thus a liveMax-Agecookie stayed forever. - Cookies: the
cookieheader now has a stable order: longest path first, then creation order. The old jar used a hash map, thus the order changed between requests. - Cookies: an
http://origin can no longer set aSecurecookie or replace one. The old jar stored these cookies and filtered them only at send time. - Range requests: a request with a
Rangeheader now sendsaccept-encoding: identity. This replaces your value. - Errors: a DNS failure now gives
dns resolution error: ...in the:reasonfield ofCloakedReq.AdapterError. No Elixir code matches this text. - The crate
rust-versionis now 1.98, becausewreq0.16 andwreq-util0.2.0 need it. This applies only if you build the NIF from source withCLOAKED_REQ_BUILD. The precompiled NIFs do not change. - Update the other Rust dependencies:
http1.4.2 to 1.5.0,psl2.1.219 to 2.1.226,lru0.18.1 to 0.18.3,flate21.1.9 to 1.1.10, and transitive patch releases. Update the Elixir dev tools:styler,sobelow,ex_slop.
[0.5.1] - 27.06.2026
Added
:chrome_149impersonation profile, picked up from thewreq-util3.0.0-rc.13 update.
Changed
- Update the Rust NIF dependencies (
wreq-utilto 3.0.0-rc.13, plushttpandpslpatch releases) and refresh Elixir dependencies (reqto 0.6.2 and the dev tooling) to latest. Thewreq-utilbump is additive: it adds the Chrome 149 profile and changes nothing for existing profiles.
[0.5.0] - 27.06.2026
Added
CloakedReq.Pool(new/1,new!/1) and a:poolattach option for a dedicated, isolated client and connection pool. By default requests share a bounded client cache; a pool gives one caller its own client whose connections, TLS session cache, and HTTP/2 multiplexing are never shared with another identity. The pool fixes the fingerprint at build time (per-request:impersonateand:insecure_skip_verifyare ignored when a pool is set), and the BEAM garbage-collects the client when the pool struct is no longer referenced, so a crashing worker cannot leak it. Proposed by @notmyactual in #32, with a reference implementation on their fork that informed this design.
[0.4.2] - 11.06.2026
Added
- Support Req 0.6: the version constraint is relaxed to
~> 0.5 or ~> 0.6. Req 0.6's changes are confined to its request/response step pipeline (multipart escaping, decoder defaults, opt-in decompression), none of which touch the adapter contract; the full suite passes against Req 0.6.1.
Fixed
- A malicious
Content-Lengthresponse header can no longer abort the BEAM VM. The response buffer is no longer pre-allocated from the raw header value, which was only reachable undermax_body_size: :unlimited. - A crafted
Set-CookieDomainattribute containing a multibyte character no longer fails cookie-jar requests; the attribute is now parsed on a character boundary. - A native request that never delivers a reply now returns a
:transport_errorafter a deadline instead of blocking the calling process indefinitely.
Changed
- The native client cache no longer grows without bound under proxy, source-IP, or connect-timeout rotation. Proxy and source IP are applied per request (wreq's connection pool already isolates connections by both), the remaining cache is LRU-bounded, and clients are built outside the cache lock.
- Request bodies are copied on the Tokio worker thread instead of the calling BEAM scheduler, keeping scheduler work constant for any body size.
- Update the Rust NIF to rustler 0.38 (a clean drop-in: no source or API changes, no new warnings).
Documentation
- Document that
max_body_sizecaps both the request and the response body. - Update the install snippet to
~> 0.4.0and regenerate the impersonation-profile list againstwreq-util 3.0.0-rc.12.
[0.4.0] - 23.05.2026
Changed
- Run native HTTP requests asynchronously on the shared Tokio runtime instead of blocking DirtyIo NIF calls, improving high-concurrency throughput.
- Abort in-flight native requests when the BEAM caller exits before the response is delivered.
Fixed
- Respect Req
connect_optionsfor proxy configuration, proxy headers, and connect timeout in the nativewreqadapter. - Reject unsupported
connect_optionswith clear adapter errors instead of silently ignoring them.
[0.3.2] - 07.03.2026
Added
- Ship
usage-rules.mdwith the Hex package so consumers and LLM tooling can discover the canonical adapter usage and options.
Changed
- Add a
HexDocspackage link in Hex metadata and a docs link near the top of the README. - Update dev dependencies:
credo,styler, andusage_rules.
[0.3.1] - 07.03.2026
Fixed
- Redirect cookies are now stored against the actual response host, so cookie jars behave correctly across host-changing redirects.
- CI again tests the minimum advertised Elixir version (
~> 1.19).
Changed
- Releases are now tagged automatically when a version bump lands on
main, and the release flow is documented inRELEASE.md.
[0.3.0] - 24.02.2026
Added
:local_addressoption for outbound source IP binding (IPv4/IPv6 tuples and strings).- Local address is included in the client cache key to prevent IP leakage through connection pooling.
[0.2.0] - 23.02.2026
Changed
- Upgraded NIF version to 2.17.
Fixed
- Release workflow now creates GitHub releases on
workflow_dispatchruns. - Release tarball naming aligned with rustler_precompiled expectations.
[0.1.0] - 20.02.2026
Added
- Req adapter with
CloakedReq.attach/2andCloakedReq.impersonate/2. - Browser emulation wiring and structured response/error mapping.
- Cookie jar support (
CloakedReq.CookieJar) with PSL-based domain validation. - Client pooling with TLS session resumption and HTTP keep-alive.
- Configurable
max_body_sizeoption (default 10 MB). - Explicit
:insecure_skip_verifyoption (defaultfalse) for constrained external test environments.