When an HTTPS endpoint works in a browser but throws certificate errors in an app, it’s usually not because the certificate is invalid. Instead, apps enforce stricter requirements than browsers, and iOS and Android implement these strictness measures differently.
Understanding these differences can save you from the frustration of thinking “the server is fine” when the issue lies in platform-specific configurations.
Core Differences
| Comparison Item | iOS (ATS) | Android |
|---|---|---|
| Default Policy | ATS is enabled by default with strict requirements | Relaxed by default, but configurable to be stricter |
| Main Requirements | TLS 1.2 or higher; Forward Secrecy required | Determined by networkSecurityConfig |
| User-Installed CA | Requires manual user trust | Apps do not trust user CAs by default (configurable) |
| Clear-text HTTP | Blocked by default | Restricted by default in newer versions |
| Exception Handling | Configure exceptions in Info.plist | Use network_security_config.xml |
iOS: What ATS Requires
ATS (App Transport Security) generally requires connections to meet the following criteria:
- TLS version must be 1.2 or higher
- Cipher suites supporting Forward Secrecy
- Certificates must use strong signature algorithms and adequate key lengths
- Certificate chain must be complete and valid
If these conditions are not met, requests from the app will fail directly, and the console will display ATS-related errors.
Troubleshooting Tools
# macOS built-in tool to diagnose if a domain meets ATS requirements
nscurl --ats-diagnostics --verbose https://api.example.com
This command tests various combinations item by item and provides clear conclusions, making it the most direct way to troubleshoot ATS issues.
Server-Side Self-Check
# ① TLS Version (must be 1.2+)
openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_2 </dev/null
# ② Check if cipher suites support Forward Secrecy (look for ECDHE/DHE)
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null
| grep -i 'cipher'
# ③ Certificate chain must be complete
openssl s_client -connect api.example.com:443 -servername api.example.com
-verify_return_error </dev/null 2>/dev/null | grep -i 'verify return'
# ④ Key length and signature algorithm
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null
| openssl x509 -noout -text | grep -iE 'public key|signature algorithm'
Configuration Recommendations
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
The cipher suite must include ECDHE—this is key to satisfying the Forward Secrecy requirement.
Android: Network Security Configuration
Android’s behavior is determined by networkSecurityConfig. A significant change in newer versions is that apps do not trust user-installed CA certificates by default.
The direct consequence: During development and packet capture, even if you install Charles/Fiddler certificates in the system, the app will still report certificate errors because it does not trust user CAs.
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<!-- Production: Trust only system CAs -->
<base-config cleartextTrafficPermitted="false" />
</network-security-config>
<!-- Debug: Allow user CAs for debugging -->
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>
<!-- Reference in AndroidManifest.xml -->
<application
android:networkSecurityConfig="@xml/network_security_config"
... >
</application>
Key Practice: Use debug-overrides to allow packet capturing in debug builds while keeping production builds secure. This facilitates development without compromising security policies in production.
Common Symptoms and Causes
| Symptom | Possible Cause |
|---|---|
| Error on iOS only, Android works | ATS has stricter requirements (TLS version / Forward Secrecy) |
| Error on Android only | Network security configuration restrictions or blocked clear-text traffic |
| Works in browser, fails on both platforms | Incomplete certificate chain (browsers often auto-complete this) |
| Packet capture fails during debugging | Android apps do not trust user CAs by default |
| Fails on old devices, works on new ones | Older OS versions may not trust newer root certificates |
Decision Tree
App reports certificate error
├─ Is it working in the browser?
│ └─ Yes, but fails in App → App has stricter requirements, or browser completed the chain
├─ Fails on iOS only
│ ├─ Diagnose with nscurl --ats-diagnostics
│ ├─ Is TLS ≥ 1.2?
│ ├─ Do cipher suites support Forward Secrecy (ECDHE)?
│ └─ Are key length/signature algorithms compliant?
├─ Fails on Android only
│ ├─ Is networkSecurityConfig restrictive?
│ └─ Is clear-text HTTP being used?
├─ Packet capture fails during debugging
│ └─ Trust user CA via debug-overrides (debug build only)
└─ Fails on both platforms
└─ Return to general troubleshooting: validity period, chain completeness, domain match
Scenario Analysis: Same Endpoint, Different Results
Observation: The same HTTPS endpoint works for one platform’s client but consistently fails for another. Server logs only show handshake-level errors with no application-layer details.
- Identify the other party’s requirements first → The two platforms have different transport-layer security requirements. One is lenient enough to pass; the other is stricter and rejects the connection.
- Verify server configuration item by item → Check protocol versions, cipher selection, and hash algorithms individually, rather than just checking if “the certificate is valid.”
- Discover relaxed configurations for legacy compatibility → To support older devices, the server may have enabled looser options that fail the stricter platform’s requirements.
- Confirm this is a configuration trade-off, not a failure → The server itself is functioning correctly; it’s a balance between “compatibility” and “strict requirements.”
- Adjust to a range that satisfies both sides → Raise the baseline while retaining necessary compatibility capabilities.
- Re-verify using real behavior from both ends → Don’t just verify against the passing side; use the stricter side as the standard.
# Item-by-item verification: Protocol version and ciphers
echo | openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_2 2>&1
| grep -E 'Protocol|Cipher|Verify return code'
# Check if low versions are rejected
echo | openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_1 2>&1
| grep -E 'Protocol|alert|failure' | head -3
# Verify certificate and signature algorithm compliance
echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null
| openssl x509 -noout -text | grep -iE 'signature algorithm|public key'
Root Cause: The server configuration was loosened to support legacy clients, falling below the minimum requirements of one of the platforms. The “different performance on both ends” is a direct reflection of this configuration choice.
Two Key Takeaways:
- When results differ between platforms, adjust based on the stricter end. Passing the lenient check doesn’t mean compliance; only passing the strict check ensures true usability.
- “Supporting legacy clients” is a trade-off with costs, not a default option. Before lowering security baselines, confirm which platform calls will be affected, otherwise failures will occur in hidden ways.
8 Common Pitfalls
- Assuming it’s fine because it works in the browser: App requirements are stricter.
- Retaining weak cipher suites: Triggers ATS failures.
- Incomplete certificate chain: Browsers auto-completing the chain masks the issue.
- Not using nscurl for diagnosis: Guessing wastes time.
- Relaxing security policies in production: Debug configurations should never enter production.
- Assuming interface issues when packet capture fails: It might be that user CAs are not trusted.
- Ignores root certificate trust on old devices: Older systems may not trust new roots.
- Clear-text HTTP is blocked in apps: Newer versions restrict this by default; ensure full-site HTTPS.
Real Customer Feedback (From Cloud Market Reviews and Consultations)
The following excerpts are taken from real buyer reviews and consultations for similar operations services on Alibaba Cloud, Tencent Cloud, and Huawei Cloud marketplaces (service providers include our store and peers, provided for reference only, not fictional tickets):
s****2 Rating: 5.0 Feb 28, 2023 11:04 Mai****8 Rating: 5.0 Jun 29, 2020 17:44 4****6 Rating: 5.0 Jan 07, 2019 14:56
a****8 Rating: 5.0 Aug 22, 2025 14:43 r****8 Rating: 5.0 May 22, 2025 15:34 t****c Rating: 5.0 Apr 18, 2025 13:58 Cai****Kang Rating: 5.0 Aug 31, 2024 12:05 2****2 Rating: 5.0 Feb 21, 2024 11:08