The Problem: An Unexpected Configuration Conflict
Recently, our monitoring dashboard started lighting up with sporadic network error logs. They weren't your typical 404s or 500s. They were fragmented, low-level errors pointing to one specific culprit: SSL Handshake Failures.
This was unexpected because our ATS (App Transport Security) configuration appeared to be fully permissive. We had explicitly set NSAllowsArbitraryLoads to true in our Info.plist.
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Theoretically, this setting is supposed to bypass ATS checks, allowing connections to servers with self-signed certificates or older TLS versions. However, despite this configuration, we were still seeing persistent TLS/SSL network errors.
After digging through the documentation, I discovered a hidden override in Apple's configuration rules.
The Discovery: The InWebContent Override
The root cause lay in another key we had enabled for our WebViews: NSAllowsArbitraryLoadsInWebContent.
We had enabled this to allow mixed content in our in-app browser. However, a crucial detail in the Apple Documentation states:
"If
NSAllowsArbitraryLoadsInWebContentis present, the value ofNSAllowsArbitraryLoadsis overridden to NO."
Because we target iOS 10+, the presence of the InWebContent key was silently overruling our global "Allow All" setting.
- Inside WebViews: Arbitrary loads were allowed.
- Native Networking (API calls, Image loading): The global flag was ignored, so ATS reverted to its default, strict mode.
This meant our app was enforcing strict ATS policies (TLS 1.2+, Forward Secrecy required) for all API calls, regardless of our intention to allow arbitrary loads.
Why TLS Settings Were the Suspect
Once it became clear that strict ATS was active, the "SSL Handshake Failure" logs made perfect sense.
- The App (Client): Enforcing TLS 1.2 or higher (ATS Default).
- The Server: A legacy server capable of speaking only TLS 1.0.
- The Result: Negotiation failed. Connection dropped.
We were inadvertently blocking our own legacy servers because the app's security standards were silently defaulted to maximum.
You might be thinking, "Who even runs TLS 1.0 servers in 2026?"
More services than you'd expect. That old payment gateway your finance team refuses to migrate? TLS 1.0. The municipal API for address verification? TLS 1.0. That ad SDK you integrated three years ago and forgot about? Yep, probably TLS 1.0.
The Misconception: "Insecure" ≠ "Old Protocol"
A common approach to fixing this is enabling NSExceptionAllowsInsecureHTTPLoads for the problematic domains. It is often assumed that allowing "Insecure Loads" disables all checks, including TLS version requirements.
This is incorrect.
NSExceptionAllowsInsecureHTTPLoads allows HTTP (unencrypted) connections to the specified domain.
However, it does not:
- Lower the TLS version requirement (HTTPS still requires TLS 1.2)
- Bypass certificate validation (self-signed or expired certs will still fail)
If the server only speaks TLS 1.0, the handshake will still fail regardless of this setting.
The Solution: Surgical Configuration
Instead of trying to force a global "Allow All" again (which is generally bad practice), I applied a surgical fix.
The goal was to:
- Keep security high: Certificate verification must remain active.
- Lower compatibility barriers: Explicitly allow older protocols for specific legacy domains.
Here is the final Info.plist configuration:
<key>NSExceptionDomains</key>
<dict>
<key>legacy-api.example.com</key>
<dict>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.0</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
This configuration instructs the app to check the certificate strictly, but to accept connections even if they use the older TLS 1.0 protocol.
Deploy & Next Steps
The updated configuration has been deployed.
If the hypothesis is correct, the SSL/TLS errors for those legacy domains should vanish. If the errors persist, there may be another layer to the problem—perhaps a cipher suite mismatch or an intermediate certificate issue.
I'll update this post with the results. Fingers crossed this is the end of the saga! 🤞
Summary
- Watch out for overrides:
NSAllowsArbitraryLoadsInWebContentsilently disablesNSAllowsArbitraryLoads. - Know your keys:
InsecureHTTPLoadsallows HTTP connections, butNSExceptionMinimumTLSVersionis required for TLS version issues. - Be specific: Don't disable security globally. Configure exceptions only for the domains that actually need them.
References
Top comments (0)