Modern Android blocks plain HTTP by default, which is a good thing, and the moment an app re-enables it the protection is gone. Since Android 9, cleartext traffic is off unless you explicitly turn it back on, usually with android:usesCleartextTraffic="true" or a permissive network_security_config.xml. AI builders and quick fixes sometimes flip that switch to make a non-HTTPS endpoint work, which quietly exposes user data to interception. Here is how Android's network security config works and how to keep cleartext off where it belongs.
Short answer
By default since Android 9 (API level 28), Android blocks cleartext (plain HTTP) traffic, so apps must use HTTPS unless they explicitly opt back in. Per Android's network security config documentation, you control this with a network_security_config.xml file or the android:usesCleartextTraffic manifest flag, which can permit cleartext globally or per domain, configure trusted certificate authorities, and set up certificate pinning. The security risk is re-enabling cleartext, which exposes traffic to interception on untrusted networks. Keep cleartext disabled, use HTTPS everywhere, and if a legacy endpoint truly needs HTTP, scope the exception to that one domain rather than the whole app.
What you should know
- Cleartext is off by default: since Android 9, plain HTTP is blocked.
- You can re-enable it: via the manifest flag or a permissive config.
- Re-enabling exposes traffic: HTTP can be intercepted on the network.
- Scope exceptions narrowly: permit cleartext per domain, never globally.
- The config does more: trusted CAs and certificate pinning live here too.
What is network_security_config.xml?
It is an XML file where you declare your app's network security policy without changing code. Referenced from your manifest, it lets you set whether cleartext traffic is allowed, and for which domains; which certificate authorities your app trusts, including custom or user-added ones; and certificate pinning rules that restrict which certificates are accepted for a domain. It also supports debug overrides that apply only in debuggable builds, so you can loosen rules for local testing without affecting release. So the config is the central place to express how your app handles transport security, and the safest posture, HTTPS everywhere with no cleartext, is also the default if you do nothing to weaken it.
How does cleartext get re-enabled, and what is the risk?
Through a manifest flag or a permissive config, and the risk is interception. The table lists the patterns.
| Setting | Effect | Risk |
|---|---|---|
| No cleartext config (default) | HTTP blocked, HTTPS only | Safe baseline |
| android:usesCleartextTraffic="true" | HTTP allowed app-wide | High; all traffic can be cleartext |
| cleartextTrafficPermitted true for all domains | HTTP allowed broadly in the config | High; same broad exposure |
| cleartextTrafficPermitted true for one domain | HTTP allowed for that domain only | Limited; scoped to a legacy endpoint |
The dangerous patterns are the broad ones: a global usesCleartextTraffic="true" or a config that permits cleartext for all domains re-opens plain HTTP across the app, so any request can travel unencrypted and be read or modified by someone on the same network. AI-generated apps sometimes set this to make a non-HTTPS backend work, trading user security for convenience.
How do you configure it securely?
Keep the default, and make any exception as small as possible. The secure baseline is to use HTTPS for every endpoint and not set usesCleartextTraffic or any cleartext permission at all, so Android's default block stays in force. If you genuinely must talk to a legacy server over HTTP, do not enable cleartext globally; instead add a network_security_config.xml that permits cleartext only for that specific domain, leaving the rest of the app HTTPS-only. Use debug-overrides for anything you need during local development so it never ships in release. And consider using the same config to add certificate pinning for sensitive domains. The goal is that the only cleartext your app allows, if any, is a single named exception you consciously accept.
What to watch out for
The first trap is a blanket android:usesCleartextTraffic="true", often added to make one endpoint work, which exposes the whole app's traffic; scope it instead. The second is a config that permits cleartext for all domains, which is the same mistake in a different file. The third is leaving a debug override or a local HTTP allowance in a release build. A pre-submission scan such as PTKD.com (https://ptkd.com) reads the compiled APK or AAB against OWASP MASVS and surfaces your cleartext settings and network security configuration, so you can confirm the app is not allowing plain HTTP before you ship. Switching the offending endpoint to HTTPS, or scoping the exception, is the fix.
What to take away
- Android blocks cleartext HTTP by default since Android 9, so HTTPS is the baseline unless you re-enable plain HTTP.
- You control this with
android:usesCleartextTrafficor anetwork_security_config.xml, which also handles trusted CAs and certificate pinning. - Re-enabling cleartext globally exposes your app's traffic to interception, so never permit it app-wide.
- Use HTTPS everywhere, scope any unavoidable HTTP exception to one domain, and use a pre-submission scan such as PTKD.com to confirm no broad cleartext is allowed.


