cloaked_req is a Req adapter backed by Rust wreq, focused on browser impersonation and performance.

Docs: https://hexdocs.pm/cloaked_req

Goal

Keep Req ergonomics while swapping transport to Rust wreq for impersonation and fingerprint-sensitive requests.

Installation

def deps do
  [
    {:cloaked_req, "~> 0.3.2"}
  ]
end

Usage

Use as a Req adapter:

request =
  Req.new(url: "https://tls.peet.ws/api/all")
  |> CloakedReq.attach(impersonate: :chrome_136)

response = Req.get!(request)

Set impersonation later on an existing request:

request =
  Req.new(url: "https://example.com")
  |> CloakedReq.impersonate(:firefox_136)

Adapter Options

OptionTypeDefaultDescription
:impersonateatomnilBrowser profile (e.g. :chrome_136)
:cookie_jarCookieJar.t()nilAutomatic cookie persistence across requests
:insecure_skip_verifybooleanfalseSkip TLS certificate verification
:local_addressIP string or IP tuplenilBind outbound requests to a specific source IP
:max_body_sizepos_integer | :unlimited10 MBMax response body size

Req's :receive_timeout (default 15s) is also respected.

Req Connect Options

CloakedReq respects these Req :connect_options:

  • :timeout - socket connect timeout in milliseconds, default 30s
  • :proxy - {:http | :https, host, port, []} proxy tuple

  • :proxy_headers - proxy headers, commonly used for proxy authentication

Unsupported connection options fail with an adapter error instead of being silently ignored.

Req.new(
  url: "https://example.com",
  connect_options: [
    timeout: 5_000,
    proxy: {:http, "proxy.example.com", 8888, []},
    proxy_headers: [{"proxy-authorization", "Basic " <> Base.encode64("user:pass")}]
  ]
)
|> CloakedReq.attach(impersonate: :chrome_136)
|> Req.get!()
Req.new(url: "https://example.com")
|> CloakedReq.attach(local_address: {127, 0, 0, 1})

Cookies are automatically stored from set-cookie response headers and sent with subsequent requests sharing the same jar. The jar uses PSL-based domain validation — it rejects cookies set on public suffixes and cross-origin domains.

jar = CloakedReq.CookieJar.new()

# Login — server sets session cookie
Req.new(url: "https://example.com/login")
|> CloakedReq.attach(impersonate: :chrome_136, cookie_jar: jar)
|> Req.post!(body: "user=admin&pass=secret")

# Dashboard — session cookie sent automatically
Req.new(url: "https://example.com/dashboard")
|> CloakedReq.attach(impersonate: :chrome_136, cookie_jar: jar)
|> Req.get!()

Impersonation Profiles

Profiles based on wreq-util 3.0.0-rc.10.

Chrome

:chrome_100, :chrome_101, :chrome_104, :chrome_105, :chrome_106, :chrome_107, :chrome_108, :chrome_109, :chrome_110, :chrome_114, :chrome_116, :chrome_117, :chrome_118, :chrome_119, :chrome_120, :chrome_123, :chrome_124, :chrome_126, :chrome_127, :chrome_128, :chrome_129, :chrome_130, :chrome_131, :chrome_132, :chrome_133, :chrome_134, :chrome_135, :chrome_136, :chrome_137, :chrome_138, :chrome_139, :chrome_140, :chrome_141, :chrome_142, :chrome_143, :chrome_144, :chrome_145

Edge

:edge_101, :edge_122, :edge_127, :edge_131, :edge_134, :edge_135, :edge_136, :edge_137, :edge_138, :edge_139, :edge_140, :edge_141, :edge_142, :edge_143, :edge_144, :edge_145

Opera

:opera_116, :opera_117, :opera_118, :opera_119

Firefox

:firefox_109, :firefox_117, :firefox_128, :firefox_133, :firefox_135, :firefox_private_135, :firefox_android_135, :firefox_136, :firefox_private_136, :firefox_139, :firefox_142, :firefox_143, :firefox_144, :firefox_145, :firefox_146, :firefox_147

Safari

:safari_16, :safari_18, :safari_ipad_18, :safari_26, :safari_ipad_26, :safari_ios_26

OkHttp

:okhttp_5

Limitations

  • No HTTP/3 / QUIC — wreq supports HTTP/1.1 and HTTP/2 only. QUIC transport fingerprinting (JA4QUIC) is not available. If HTTP/3 fingerprinting is critical for your use case, consider a Go-based alternative like surf which supports HTTP/3 with full QUIC fingerprinting — though it would require a different integration approach (sidecar/Port rather than NIF).