SReverseby Simpa Labs

Traffic interception

Charles Proxy works for the browser but not the app

The certificate is installed and a browser request shows readable traffic. The same proxy shows the app's host as an opaque tunnel, or shows nothing at all. Which of those two you see decides where to look.

What the browser result rules out

When a browser request appears decrypted in Charles, the proxy is reachable, the certificate is installed in the user store, and the machine's proxy settings are correct. That result rules out a broken installation and a blocked port. It says nothing about the app, because a browser and an app decide independently whether to trust a certificate authority.

Android separates user-installed authorities from system ones. An app that does not declare a network security configuration trusts only the system store on current Android versions. A certificate installed from the device settings lands in the user store, so the browser accepts it and the app aborts the handshake. Charles often shows a CONNECT line for that host with nothing readable after it.

Read the session list first

How the app's host appears in Charles narrows the cause faster than any other observation.

  • The host never appears. The app is not sending that traffic through the proxy at all. Some apps choose a proxy per connection in code, and some use a transport that ignores the system proxy.
  • A CONNECT line appears and the request stays opaque. The app reached the proxy and refused the certificate, or accepted the connection and then rejected the server certificate through pinning.
  • The request decrypts and a later call in the same flow fails. Transport is working. The problem sits in the signature, token or session layer, and the proxy was never the obstacle.

Write down which of those three you see before you change a setting, because the fixes do not overlap.

Test whether the app uses the system proxy

Set a proxy port that is closed in the device settings, then open the app. If data still loads, the app bypasses the system proxy and Charles will never see the traffic as configured. Emulators accept a proxy flag on the command line, and a rooted test device can redirect traffic to the proxy at the network layer. A global redirect catches connections the app opens itself.

If the app stops loading data with the closed port, it does honour the system proxy and you can move to the trust question.

Separate trust from pinning

Both failures look the same in the session list, and one test distinguishes them. Move the certificate into the system store on a rooted device or an emulator with a writable system partition, then restart the app. If decryption begins, the app trusts system authorities and nothing else, and the network security configuration is the thing to read. If decryption still fails, the app is validating a specific certificate or public key, which no trusted authority can satisfy.

Pinning appears in several places, and the location decides the method. OkHttp pins through a certificate pinner on the client builder. A custom trust manager or a native library pins outside that path. A network security configuration can declare a pin set in XML that the platform enforces. Read the XML first because it survives in the APK as plain text, then check the client builder.

Frameworks that ignore the usual tools

An app built with Flutter, React Native or a native networking stack does not route its calls through the Java HTTP client, and the standard pinning patches miss it. Flutter keeps network logic in the compiled Dart library, and a native client keeps it in a shared object. In those apps the certificate decision happens somewhere that expects a different set of hooks. Flutter API reverse engineering and native Android reverse engineering describe where that logic sits.

When capture is the wrong tool

Interception is a means to an end, and the end is usually a request you can send yourself. If the app pins in a way that resists patching, you can recover the signing routine from the APK and reproduce the request without reading a decrypted frame. A captured session also has a limited life against server state. What you need from it is the order of calls and the values that pass between them, and a reconstruction of that flow gives you those. Capture answers where each value came from, and the rebuilt flow answers what to send when.

Reviewed 28 September 2026 · SReverse research desk

Start your full APK reconstruction

Send the full APK

Projects start at $120. Choose WhatsApp or email, then attach the APK in the app that opens. We reply within one hour with the next step and send the fixed quote after review.

Want us to contact you?