You set the emulator's proxy, installed your proxy's CA under Settings → Security, opened the app you're debugging, and the requests still don't show up. Or worse, the app dies with javax.net.ssl.SSLHandshakeException: Trust anchor for certification path not found.
You didn't do anything wrong. Since Android 7 (Nougat), apps ignore the CA you just installed. This guide walks through why that happens and the two approaches that actually work, with copy-pasteable commands. It's written for the emulator specifically, but everything here applies to a physical device too. Only the transport differs.
The 30-second version
| You want to intercept… | Do this |
|---|---|
| An app you build (debug APK) | Add a network_security_config.xml that trusts user certs. 5 minutes, no root. |
| Any app (third-party, release build) | Boot a Google-APIs emulator image with -writable-system, install the CA into the system store. More steps, needs root. |
| Just a browser / WebView | User-cert install often works; test before assuming. |
Both paths need two things in place first: the emulator pointed at a proxy, and the proxy's CA trusted the right way. Let's do the proxy part once, then pick a trust path.
Why HTTPS interception on Android is harder than it looks
An intercepting proxy is a deliberate man-in-the-middle. When your app opens an HTTPS URL, the request is tunnelled to the proxy with an HTTP CONNECT; the proxy then terminates TLS itself, presenting a certificate it minted on the fly, signed by its own certificate authority, before opening a second TLS connection out to the real server. Your client sees a valid chain only if it trusts that CA. That's the whole game: get the client to trust the proxy's CA, and HTTPS becomes readable.
On desktops and older Android, you dropped the CA into the trust store and every client honored it. Then Android 7 changed the default. Every app now ships with an implicit network_security_config that trusts the system CA store only:
<!-- The implicit default for apps targeting Android 7+ -->
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
Notice what's missing: <certificates src="user" />. A CA you install through Settings → Security → Encryption & credentials → Install a certificate lands in the user store. Apps targeting API 24+ don't look there. That's the entire reason "I installed the cert and it still doesn't work" is one of the most-searched Android debugging problems.
So you have exactly two levers: make the app trust user certs, or get the CA into the system store where every app already looks. Which lever you can pull depends on whether you own the app.
Step 1: Point the emulator at your proxy
Do this regardless of which trust path you take. There are three ways; pick whichever fits your workflow.
A. Extended controls (clickable). Open the emulator's ⋮ (More) panel → Settings → Proxy → uncheck "Use Android Studio HTTP proxy settings" → Manual proxy configuration → set host and port.
B. adb (scriptable, best for CI and repeatable setups).
# Route the emulator's HTTP/HTTPS through a proxy on your machine:
adb shell settings put global http_proxy 10.0.2.2:8080
# Clear it again when you're done:
adb shell settings put global http_proxy :0
C. Boot flag.
emulator -avd Pixel_7_API_34 -http-proxy http://10.0.2.2:8080
D. Emulator Wi-Fi proxy (fallback). On the emulator, long-press the connected AndroidWifi network → Modify network → Advanced options → Proxy: Manual → enter host and port. Reach for this when a newer system image ignores the global http_proxy setting from option B.
The 10.0.2.2 gotcha
Inside the emulator, 127.0.0.1 is the emulator itself, not your laptop. The host machine's loopback is aliased to 10.0.2.2. So a proxy running on your own machine (mitmproxy, Charles, Proxyman, a local Proxycept dev instance) is reachable at 10.0.2.2:<port>, never localhost. (A physical device on the same Wi-Fi uses your machine's LAN IP instead, e.g. 192.168.1.x. And for reference, the iOS Simulator is the opposite of both: it shares the host network, so 127.0.0.1 just works.)
If your proxy is hosted rather than running on your laptop, you skip this quirk entirely: you point at a real hostname and port, and the exact same value works from the emulator, a physical device, and CI. With Proxycept, for example, that's the public edge host on port 8443 with your profile's credentials. No 10.0.2.2 translation, no "works on my machine but not in the pipeline."
Sanity check before touching certs: proxy some plain HTTP first.
adb shellthencurl http://neverssl.com(or just loadneverssl.comin the emulator browser). If that doesn't appear in your proxy, fix the proxy wiring before blaming TLS.
Step 2: Make the app trust your CA
Path A: Your own debug app (easiest, no root)
If you control the app's source, this is the clean way and it takes five minutes. Add a network security config that trusts user certs, scoped to debug builds so you never ship it:
<!-- app/src/main/res/xml/network_security_config.xml -->
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>
Reference it in the manifest:
<!-- AndroidManifest.xml -->
<application
android:networkSecurityConfig="@xml/network_security_config"
... >
<debug-overrides> applies only when the build is android:debuggable="true", so your release builds keep validating against system CAs exactly as before. (Prefer file-based scoping? Drop the same network_security_config.xml in src/debug/res/xml/ instead. It compiles into debug builds only and can never ship to production.)
To install the CA, first get the PEM onto the emulator: adb push proxycept-ca.pem /sdcard/Download/proxycept-ca.crt (the .crt extension helps the installer recognize it). Then go to Settings → Security → Install a certificate → CA certificate, pick it from Downloads, and tap Install anyway past the warning. Your debug app will now trust the proxy.
Prefer not to depend on what's installed on the device? Bundle the CA in the app and trust it directly. This is what the Proxycept Android sample does, pinning a PEM in res/raw alongside src="user":
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
<certificates src="@raw/proxycept_ca" />
</trust-anchors>
That variant means the app trusts the proxy CA even on a fresh emulator with nothing installed, which is handy for a repeatable CI image.
Path B: Any app (system-store CA install, needs root)
To intercept an app you don't control, you have to put the CA where every app already looks: the system store. That requires a rootable emulator.
1. Use a Google-APIs image, not a Google-Play image. Only non-Play "Google APIs" (or AOSP) system images allow adb root. Play-Store images are locked down: adbd cannot run as root in production builds is the error you'll hit. Create the AVD accordingly.
2. Boot with a writable system partition:
emulator -avd Pixel_7_API_34 -writable-system -http-proxy http://10.0.2.2:8080
3. Get the CA into the right filename. Android looks up system CAs by the old OpenSSL subject hash, with a .0 extension. Convert your PEM:
# Produces something like 9a5ba575
hash=$(openssl x509 -inform PEM -subject_hash_old -in proxycept-ca.pem | head -1)
cp proxycept-ca.pem "$hash.0"
4. Remount, push, permission, reboot (Android 13 / API 33 and below):
adb root
adb remount
adb push "$hash.0" /system/etc/security/cacerts/
adb shell chmod 644 /system/etc/security/cacerts/"$hash.0"
adb reboot
After reboot the CA is a system trust anchor and every app validates the proxy's certificates, with no per-app config.
The Android 14 nuance (don't skip this if you're on API 34+)
Android 14 moved the system CA store into an updatable APEX module: the certs now live under /apex/com.android.conscrypt/cacerts/, which a plain adb remount does not make writable. The push-to-/system/etc/security/cacerts recipe above silently won't take on API 34+.
The working approach copies the existing apex cacerts into a tmpfs, adds your hashed cert, then bind-mounts the result over /apex/com.android.conscrypt/cacerts (and mirrors that mount into already-running app namespaces). It's fiddly and it keeps shifting with patch levels, so rather than paste a script that rots, use a maintained community tool for this step. Search "Android 14 system certificate conscrypt apex": HTTP Toolkit and others publish current scripts. The honest summary: on API ≤ 33 the four commands above are stable; on API 34+ budget extra time and lean on a maintained script.
Simplest way to dodge the whole apex saga: if you own the app, use Path A. System-store surgery is only worth it for apps you can't rebuild.
Step 3: Verify it actually works
Fastest proof, straight from the emulator shell:
adb shell
# inside the emulator:
curl https://example.com/ # should return 200 through the proxy, no cert error
No handshake error means the CA is trusted. Now confirm the proxy is really in the path by checking who signed the leaf certificate. When intercepted, it's your proxy's CA, not the real issuer. The Proxycept sample surfaces exactly this: it prints the TLS issuer, and an intercepted request to example.com comes back signed by CN=Proxy Control CA instead of the real authority. The same request shows up live in the proxy's capture view as GET 200 example.com/.
Want proof the proxy can edit traffic and not just watch it? Add a rule that rewrites a response body, fire the request again, and watch the tampered body arrive in the client. That is undeniable evidence the proxy is terminating TLS in the middle.
If you see the request and the issuer is your proxy's CA, you're done. Every subsequent HTTPS call is now readable and editable.
Troubleshooting & FAQ
Why is HTTPS on my Android emulator not working even after installing the certificate?
Because you installed it into the user store, and apps targeting Android 7+ only trust the system store by default. Either trust user certs in the app's network_security_config (Path A) or install the CA into the system store (Path B).
What does "Trust anchor for certification path not found" mean? The app doesn't trust the CA that signed the certificate it received, i.e. it doesn't trust your proxy's CA. Same root cause as above; same two fixes.
Why does adb root say "adbd cannot run as root in production builds"?
You're on a Google Play system image. Recreate the AVD with a Google APIs (non-Play) or AOSP image; those allow root.
I pushed the cert to /system/etc/security/cacerts and it still doesn't work.
Two usual causes: you're on Android 14+ (the store moved to the Conscrypt APEX, as covered in the nuance above), or you booted without -writable-system so the push didn't persist. Also double-check the filename is the subject_hash_old value with a .0 extension.
Do I need root at all? No, not if you're debugging an app you build. Path A (network security config) needs no root. Root is only required to intercept apps you can't modify.
Can I intercept a release build of my own app without rooting?
Not cleanly. Release builds don't get <debug-overrides>, and you shouldn't ship a config trusting user certs. Test against a debug build, or use the system-store route.
The app uses certificate pinning. Will trusting the CA get me in? No. Pinning validates the server against a specific certificate or public key baked into the app, bypassing the trust store entirely, so even a CA the device fully trusts is rejected. Your only realistic option is a debug build with pinning disabled (loosen it in the network security config, or stub the pinner in dev); you can't pin-bypass someone else's release app on a stock device. That's the pin doing its job.
Does setting the proxy break plain HTTP too?
It shouldn't, but if an app uses cleartext HTTP on Android 9+ you may also need cleartextTrafficPermitted="true" in your network security config for that domain.
I'm pointing at a hosted/authenticated proxy and get a 407 even with the right credentials.
First verify the credentials straight from your terminal: curl -x http://USERNAME:PASSWORD@proxycept.com:8443 http://httpbin.org/get. A 200 means they're good; a 407 here means they're wrong or the profile was restarted (grab fresh ones). If curl works but your app still gets 407, you've hit a JDK/Android footgun: proxy Basic auth over an HTTPS CONNECT tunnel is silently disabled by default. Re-enable it with System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "") and answer the challenge with a proxy Authenticator (OkHttp exposes a proxyAuthenticator instead). This only bites authenticated proxies. A local, unauthenticated laptop proxy never sends the 407.
Charles / Proxyman / mitmproxy: do these change any of this? No. The CA-trust problem is Android's, not the tool's. Any intercepting proxy hits the identical Android 7 / Android 14 walls; the commands here are the same whether the proxy is Charles, Proxyman, mitmproxy, or a hosted service. The differences between tools are about where the proxy runs and what it does with the traffic (below), not about how Android trusts a CA.
Where Proxycept fits
Let's be precise, because this is where guides usually oversell. The CA-trust step above is identical no matter what proxy you use. Proxycept does not have a magic one-tap Android CA installer. On Android you install the CA manually, exactly as documented here. Anyone who tells you otherwise is selling you something.
What a hosted proxy like Proxycept removes is the other half of the setup, the part that has nothing to do with certificates:
- No laptop-as-proxy. The proxy runs on a server behind one public edge (
:8443) with per-profile credentials. Your emulator, a real device, and your CI runner all point at the same host:port, so you never fight the10.0.2.2translation or "it worked locally but not in the pipeline." - Always-on and shared. It's a team resource, not a process on one person's machine. Teammates open the same session; you send a read-only link to a captured session instead of screenshotting Charles.
- Capture, intercept, mock, fault-inject in one place. Read every request/response, hold and edit a request or response on the fly, serve OpenAPI/Postman/manual mocks per profile, and inject latency or errors, all against the emulator traffic you just wired up.
If your interception is a solo, on-my-machine thing, a desktop tool is genuinely fine. Proxycept earns its place when the proxy needs to be hosted, shared across a team, and reachable from CI. Start free and point your emulator at your profile's edge. The CA step is the one you just learned; everything after it is done for you.
Works beyond the emulator
The reason this narrow how-to matters: once one client trusts the proxy, every client can. The same capture/intercept/mock workflow runs on:
- iOS Simulator: the one platform where the cert dance genuinely disappears. Download the CA and run
xcrun simctl keychain booted add-root-cert proxy-ca-cert.pem: it goes straight into the Simulator's trusted root store, trusted immediately. No profile prompt, no Certificate Trust Settings toggle. Needs Xcode 11.4+ and a booted simulator; apps that pin their certificates still won't trust it. Proxycept also ships an optional local agent (make dev-ca-agent) that does the same thing from the UI in one click. A dedicated iOS Simulator guide is planned. - Real iOS & Android devices: same proxy, install the CA on the device and (Android) trust it per the rules above. A per-platform CA install guide is planned.
- macOS & Windows: trust the CA in Keychain / the Trusted Root store; every HTTP-proxy-aware tool then routes through.
- CI pipelines: export
HTTP_PROXY/HTTPS_PROXYand your test suite's calls are captured for inspection or replaced with mocks. A CI guide is planned.
Same CA, same profile, six environments.
Related reading
- Proxycept vs Charles: hosted/shared/CI vs a desktop app
- Proxyman alternative for teams (coming soon)
- mitmproxy in the cloud, without running it yourself (coming soon)
Neutral note on the alternatives mentioned: Charles, Proxyman, and mitmproxy are all excellent desktop/CLI interception tools and the Android steps above work identically with them. The distinction is architectural: they run on your machine; Proxycept runs hosted and shared. Pick the model that fits your team.