Blog

How an on-device HTTPS debugger actually works

Every HTTPS debugging tool does the same basic trick: sit between an app and the server it’s talking to, decrypt the TLS in the middle, and show you what’s inside. Charles, Proxyman, and mitmproxy all do this on a Mac or PC, with your phone pointed at that machine as a Wi-Fi proxy. HTTPGlass does the same trick, but the “machine in the middle” is the phone itself.

Getting the packets in the first place

iOS doesn’t let a regular App Store app read another app’s network traffic by default — for good reason. The one sanctioned way in is Apple’s NetworkExtension framework, and specifically a Personal VPN (NEPacketTunnelProvider). Once a user approves that profile, the extension process receives every outbound IP packet from the device before it leaves for the internet, and is responsible for actually delivering it.

That “actually delivering it” part is the catch. A packet tunnel provider doesn’t get a parsed HTTP request — it gets raw IP packets. To do anything useful with a TCP connection, something has to reassemble those packets into a real socket.

A TCP/IP stack that isn’t the kernel’s

HTTPGlass carries its own userspace TCP/IP stack, built on lwIP (a small, widely-used embedded networking stack), running inside the extension process. Every intercepted TCP handshake gets terminated locally — HTTPGlass’s lwIP instance completes the three-way handshake with the app as if it were the real destination, then opens its own outbound connection to wherever the app actually meant to go.

For plain HTTP, that’s enough: read the decoded bytes as they flow through, parse the HTTP/1.1 frame, hand it to the capture pipeline.

Where TLS comes in

Most traffic isn’t plain HTTP anymore. For an HTTPS connection, HTTPGlass:

  1. Lets the app begin its TLS handshake, and reads the SNI (the hostname) out of the ClientHello before responding.
  2. Issues a leaf certificate for that exact hostname on the fly, signed by a root certificate generated once and stored locally, then completes the handshake using that leaf.
  3. Opens a second, completely separate TLS connection to the real server, and relays decrypted bytes between the two connections — logging them as they pass through.

This only works because the device trusts that root certificate — which is exactly what the certificate setup step is for. Without that trust, the app would (correctly) reject HTTPGlass’s certificate as untrusted, the same way it would reject any other unknown certificate.

HTTP/2 and the parts that didn’t work the first time

HTTP/2 multiplexes many requests over one connection and frames them in binary, which meant building a second parsing path rather than reusing the HTTP/1.1 one. The current design offers HTTP/1.1 to the app side and negotiates the best available protocol with the real origin server — including HTTP/2 — translating between the two when they differ. Offering HTTP/2 all the way through to the app turned out to be its own multi-week debugging story (mismatched event-loop semantics between two protocol stacks running in the same process); that’s staying as a documented, deliberately-deferred improvement rather than something half-shipped.

What never happens

Nothing described above touches a server HTTPGlass doesn’t control. There’s no third machine, no cloud relay, no “send your traffic to our backend to decrypt it” step. The extension process, the lwIP stack, the certificate, and the captured data all live inside the app’s own sandboxed storage on your phone.


HTTPGlass for iPhone and iPad is on the App Store. The Mac app, which uses a local proxy instead of a packet tunnel, is a separate download.

Related: Capture HTTPS traffic · Install the root certificate

Want to see this in action?