Skip to content

[feature] Preserve or impersonate original outbound TLS fingerprint to avoid Cloudflare 403 during SSL proxying #2634

@WingCH

Description

@WingCH

Problem

When using Reqable for authorized debugging of my own Android traffic, I found a case where the request works normally before enabling SSL proxying, but starts returning 403 after SSL proxying is enabled.

The important part is that SSL decryption itself appears to work correctly: Reqable can decrypt the request and I can see the full URL path. However, after the traffic is decrypted and forwarded by Reqable, the Cloudflare-protected endpoint returns 403.

Environment

  • Platform: Android
  • Device: Pixel 8 Pro
  • Android: Android 16 / API 36
  • Reqable: 3.1.2
  • App under test: Flutter / dart:io HTTP client
  • Endpoint: controlled Cloudflare Worker / Cloudflare-protected test API owned by me
  • Root / CA setup: Rooted device with Reqable CA installed; HTTPS decryption succeeds

What I observed

I enabled Cloudflare-side checks on my own test endpoint and compared the request metadata seen by Cloudflare.

Direct app request, without Reqable SSL proxying

The endpoint returns 200.

Cloudflare sees roughly this TLS profile:

user-agent: Dart/3.10 (dart:io)
httpProtocol: HTTP/1.1
tlsVersion: TLSv1.3
tlsCipher: AEAD-CHACHA20-POLY1305-SHA256
tlsClientHelloLength: 239
tlsClientCiphersSha1: Z5hJp4rGfwSOBwFfibDtLAP5rkI=
tlsClientExtensionsSha1: IDS2jshLLK/HGrjct4bpWAlzt4s=
tlsClientExtensionsSha1Le: f0GI4lWgjQCmrfuxp8ErVr/D19M=

Same request through Reqable with SSL proxying enabled

Reqable can decrypt the traffic and show the full URL path, but the endpoint returns 403.

Cloudflare sees a different TLS profile:

user-agent: Dart/3.10 (dart:io)
httpProtocol: HTTP/1.1
tlsVersion: TLSv1.3
tlsCipher: AEAD-AES256-GCM-SHA384
tlsClientHelloLength: 1533
tlsClientCiphersSha1: hCCNuWP9ky6AR69i97wdKYbhFQo=
tlsClientExtensionsSha1: M5Sm/2qrabCxy8tG1NIWsjf99Ug=
tlsClientExtensionsSha1Le: C6UcGP66kPwR41Cyi/I/iPOzh/Q=

The HTTP headers, including User-Agent, are still close enough to the original request. The major externally visible difference is the TLS ClientHello / fingerprint generated by Reqable's outbound connection to the origin through Cloudflare.

I also reproduced this on a controlled Cloudflare Worker endpoint by blocking only the observed Reqable TLS fingerprint. The same Android app request was then blocked only when routed through Reqable SSL proxying.

Expected behavior

For debugging authorized traffic, enabling SSL proxying should ideally not cause Cloudflare or similar WAF / anti-bot systems to treat the request as a substantially different client solely because the outbound TLS fingerprint changed.

Feature request / suggestion

Would it be possible for Reqable to support one of the following?

  1. Preserve or inherit the original client network-layer fingerprint as much as technically possible when forwarding the decrypted request.
  2. Provide outbound TLS fingerprint impersonation profiles, for example Android / BoringSSL / Chrome Android / Flutter dart:io style profiles.
  3. Expose advanced settings for outbound TLS ClientHello behavior, such as cipher order, TLS extensions, extension order, ALPN, GREASE behavior, signature algorithms, key share, padding, HTTP/2 settings, etc.
  4. Provide a per-host option to use a more transparent/passthrough-like mode when a site is known to be sensitive to TLS fingerprint changes.

I understand that true SSL MITM terminates TLS and therefore cannot literally reuse the original TLS session. But if Reqable can make its outbound TLS fingerprint closer to the original app/client profile, it would reduce false positives from Cloudflare/WAF checks and make Reqable more usable for debugging modern mobile APIs.

Related issues

This seems related to existing TLS fingerprint / JA3 requests:

This issue adds a concrete Cloudflare/Android/Flutter reproduction and the observed before/after Cloudflare TLS metadata.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions