Mr.AndroidShin / Dev Tools

Debugging log · Networking

"Cleartext HTTP traffic not permitted"

I pointed an app at a local test server, ran it, and every request failed instantly — before it even left the device. The URL was fine, the server was up, and the same address worked in a browser. The difference was one letter: my URL started with http://, and modern Android refuses to send that by default.

The short version: since API 28, Android blocks unencrypted HTTP unless you explicitly allow it. Use https:// wherever you can. If you truly need plain HTTP — a local dev server, for instance — allow it for that host only via a network security config, not globally.

The error

java.io.IOException: Cleartext HTTP traffic to 192.168.0.10
not permitted

"Cleartext" just means unencrypted — plain HTTP rather than HTTPS. The request never reaches the network; the platform stops it on the device.

Why Android blocks it

Anything sent over plain HTTP travels readable by every device between the phone and the server — the coffee-shop Wi-Fi, the ISP, anyone running a proxy in between. They can read it and, worse, modify it in flight. For years this was opt-out; from Android 9 (API 28) the default flipped to blocked, so apps have to consciously say "yes, I really want to send this in the clear". The error is the platform protecting your users from a mistake, which is why the fix should be narrow rather than sweeping.

Fix 1 — use HTTPS (the real answer)

If the server supports TLS, just switch the URL to https:// and the error is gone with nothing else to configure. For any production endpoint this is the only correct fix. If your own backend doesn't have a certificate yet, that's the bug to solve — certificates are free and automatic these days, and every third-party API you'd integrate already requires HTTPS anyway.

Fix 2 — allow cleartext for one host (dev servers)

Sometimes you genuinely need HTTP: a machine on your LAN, an emulator hitting 10.0.2.2, a legacy device you can't put TLS on. Allow it for that host only. Create res/xml/network_security_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">192.168.0.10</domain>
        <domain includeSubdomains="true">10.0.2.2</domain>
    </domain-config>
</network-security-config>

Then reference it from the manifest:

<application
    android:networkSecurityConfig="@xml/network_security_config"
    ... >

Every other domain stays protected. That's the important part — you've made an exception, not a hole.

Better: only in debug builds

Put the config in your debug source set (src/debug/res/xml/) so the exception physically cannot ship in the release APK. Your dev server works, and production keeps the strict default with no chance of a stray flag escaping.

Don't reach for android:usesCleartextTraffic="true". It's the most-copied answer online and it disables the protection for every host in your app, in every build, forever. It makes the error go away and leaves your users' traffic readable on any network they join. Scope the exception instead.

Notes that save time

IP addresses above are examples — substitute your own dev server's address.