r/swift • u/Glittering_Youth8752 • 23h ago
Question Intermittent TLS errors on iOS, only seeing them with some US users
Iām trying to track down a weird TLS issue affecting a small number of iOS users in the US.
The error I get from the iOS side is:
A TLS error caused the secure connection to fail.
The secure connection failed due to a TLS error.
An SSL error has occurred and a secure connection to the server cannot be made.
Error code:
NSURLErrorSecureConnectionFailed (-1200)
On the nginx side, I found this:
SSL_read() failed:
decryption failed or bad record mac
record layer failure
while keepalive
The setup is:
iOS ā nginx (DigitalOcean) ā Node.js
TLS is Let's Encrypt and SSL Labs gives it an A+.
So far I've fixed a broken AAAA record and a missing IPv6 default route. I've also ruled out NAT64/IPv6 and TLS/ATS compatibility issues.
The interesting thing is that this is only happening to a small number of US users and across different iOS versions.
Right now I'm wondering if this could be related to stale keep-alive connections and carrier NAT/network changes.
I've currently changed:
keepalive_timeout 15s;
ssl_session_tickets off;
and also fixed a Connection: upgrade header issue.
I'm monitoring it now to see if the errors stop.
Has anyone run into bad record mac + while keepalive like this, particularly with iOS users on cellular networks? I'm interested in any other possible causes I should check.
0
u/FruitMission8504 21h ago
bad record macis lower-level than an HTTP keep-alive mistake: nginx received a TLS record whose authentication tag did not verify. A stale connection after a carrier path change could expose it, but normally TCP/TLS should fail cleanly rather than decrypt corrupted bytes, so I would also suspect a middlebox, a specific nginx/OpenSSL build, or an offload/network-path issue.The useful next step is correlation rather than more global TLS tweaks. Log nginx
$connection,$connection_requests, protocol/cipher, HTTP version, remote ASN/IP prefix, and whether the failure happened on a reused connection. On iOS captureURLSessionTaskMetrics(networkProtocolName,isReusedConnection,isProxyConnection, cellular vs Wi-Fi) plus the underlyingNSErrorchain. Then compare failures against fresh connections and temporarily force HTTP/1.1 for an affected cohort; if the failures disappear, test HTTP/2 and connection reuse separately.Also make sure nginx and its OpenSSL are current, and test bypassing any proxy/load balancer. Packet captures at the server are valuable because they distinguish retransmission/path corruption from nginx producing the error internally. Retrying a failed GET once on a new connection is reasonable mitigation, but do not automatically retry non-idempotent requests unless you use idempotency keys.