Phones and operating systems
NSURLErrorDomain Code=-1202: certificate untrusted on iOS
NSURLErrorDomain Code=-1202NSURLErrorDomain Code=-1202 means Apple's URL loading system did not trust the server's certificate. The constant is NSURLErrorServerCertificateUntrusted. Apple uses it for most failed trust evaluations: private or self-signed CAs, hostname mismatches, incomplete chains and certificates that break Apple's rules. The log line usually begins "The certificate for this server is invalid."
What iOS checks before trusting a server
Apple's documentation lists the checks of default server trust evaluation: the certificate's signature is intact, it has not expired, its name matches the server's DNS name, and it chains through valid certificates to a trusted anchor. That anchor must ship with the operating system or be installed on the device by the user or an administrator. Several different failures surface as the same -1202, so the code alone does not tell you which check failed; the steps below narrow it down.
Where -1202 sits among the TLS codes
| Code | Swift name | Meaning |
|---|---|---|
| -1200 | secureConnectionFailed |
Handshake failed, see NSURLErrorDomain -1200 |
| -1201 | serverCertificateHasBadDate |
Certificate expired or not yet valid |
| -1202 | serverCertificateUntrusted |
Trust evaluation failed |
| -1203 | serverCertificateHasUnknownRoot |
Not signed by any root |
| -1204 | serverCertificateNotYetValid |
Certificate not yet valid |
| -1206 | clientCertificateRequired |
Server wants a client certificate |
Safari shows the same situation as This Connection Is Not Private.
Read the underlying SSL code in the log
URLSession logs often add a line such as NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813). That second number comes from Apple's Security framework and is more specific than -1202:
| Code | Constant | What it means |
|---|---|---|
| -9807 | errSSLXCertChainInvalid |
Invalid certificate chain |
| -9812 | errSSLUnknownRootCert |
Valid chain, untrusted root |
| -9813 | errSSLNoRootCert |
Chain not verified by a root, often a missing intermediate or private CA |
| -9814 | errSSLCertExpired |
A certificate in the chain has expired |
| -9843 | errSSLHostNameMismatch |
The host name does not match the certificate |
Common causes, most frequent first
1. Development or internal server with a private CA
Staging APIs, on-premise servers and local machines often use self-signed certificates or an internal CA. Neither is in iOS's trust store. Look at the issuer:
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer
2. Hostname or IP not covered by the certificate
The app calls https://203.0.113.10 or an alias that is not in the Subject Alternative Name. Since iOS 13, names in the Common Name are not trusted at all, so a certificate with the name only in the CN also fails. List the names:
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null \
| openssl x509 -noout -ext subjectAltName
3. Incomplete chain
The server sends only its own certificate. Some clients repair this; do not rely on it. Count the certificates with -showcerts or use the chain builder; see missing intermediate certificate.
4. The certificate breaks Apple's rules
Apple requires, for all TLS server certificates on iOS 13 and macOS 10.15 or later: RSA keys of at least 2048 bits, SHA-2 signatures, and the DNS name in the SAN. Certificates issued after July 1, 2019 also need the id-kp-serverAuth extended key usage and a validity of no more than 825 days. Publicly trusted certificates issued on or after September 1, 2020 may not exceed 398 days. Internal CAs that issue five-year certificates without an EKU fail here. Decode the certificate with the certificate decoder and check each item.
5. Debugging proxy or network interception
Charles, Proxyman, mitmproxy, corporate proxies and captive portals present their own certificates. Their root must be installed and fully trusted on the device, or the app fails with -1202.
Diagnose from a Mac
nscurl --ats-diagnostics --verbose https://api.example.com/
nscurl uses Apple's networking stack, so a trust failure there reproduces what the app sees. Compare with an external check from the SSL checker.
How to fix it
Fix the certificate or the chain on the server; never relax trust evaluation in a shipping app.
Public servers: use a certificate from a public CA, include every hostname the app uses in the SAN, and serve the full chain (fullchain.pem in Nginx).
Internal or development servers on test devices: 1. Issue certificates that follow Apple's rules: SAN with the DNS name, serverAuth EKU, SHA-256, at most 825 days. 2. Install the root CA on the device through a configuration profile or MDM. 3. For a profile installed by hand, open Settings > General > About > Certificate Trust Settings and enable full trust for that root. Profiles deployed through MDM or Apple Configurator are trusted automatically.
In the app: keep the default trust evaluation. Apple's guide to manual server trust authentication shows how a urlSession(_:didReceive:completionHandler:) delegate can inspect the server trust, but its documentation on insecure connections states that while ATS is enabled you can only tighten evaluation, for example to pin a key, not loosen it. For a private CA in production, distribute its root to managed devices instead of weakening checks in code. Never accept every certificate, not even temporarily in a release build.
How to verify the fix
openssl s_client -connect api.example.com:443 -servername api.example.com -verify_return_errorcompletes withVerify return code: 0 (ok)for public certificates.nscurl --ats-diagnostics https://api.example.com/reports PASS for the default configuration.- Run the request on a physical device as well as the simulator, since their trust stores differ.
FAQ
Can I add an ATS exception to Info.plist instead?
Not as a fix. Default server trust evaluation still requires a chain to an anchor that ships with the OS or was installed by the user or an administrator. Apple's documentation also notes that while ATS is enabled, an app can only tighten trust evaluation (for example with pinning), not loosen it. Fix the certificate or install the private root on the devices.
It works in the simulator but fails on a real device (or the reverse). Why?
The simulator and the device have separate trust stores. A development root installed on your Mac or in one simulator is not present on the device, and a root installed on a device from a profile still needs full trust enabled in Certificate Trust Settings.
Why does -1202 appear when I connect by IP address?
The certificate lists DNS names, not that IP, so the hostname check fails. Public CAs issue IP certificates only for public addresses. Connect by a DNS name that the certificate covers.
How is -1202 different from -1200?
-1200 (NSURLErrorSecureConnectionFailed) means the TLS handshake failed, often because of ATS protocol or cipher rules. -1202 means the handshake got as far as the certificate and trust evaluation rejected it.
Sources
- Apple Developer: URLError.Code.serverCertificateUntrusted
- Apple Support: Requirements for trusted certificates in iOS 13 and macOS 10.15
- Apple Support: About upcoming limits on trusted certificates
- Apple Support: Trust manually installed certificate profiles
- Apple Developer: Performing manual server trust authentication
- Apple Developer: errSSLHostNameMismatch
- Apple Developer: Preventing insecure network connections