#Sporadic "API exception 400: invalid certificate" despite valid certificate

1 messages · Page 1 of 1 (latest)

jovial crypt
#

I'm getting sporadic issues where the ios app remote-sync will fail with an api exception of 400: invalid certificate. This happens much more frequently after switching networks (home wifi to mobile data and vice versa), tho it's not isolated to any specific network. I fully close the app between this reconnect, not just minimize. I'm also using a nginx reverse proxy, which I suspected to be the issue at first, but after a while of debugging I noticed that no requests would show up in its access.log (and immich server logs) when this errror would occur, so it seems the request isn't even reaching my server in those cases.

I've tried testing without a reverse proxy, but since that prevents me from accessing it from external networks, and there's no SSL, it's an imperfect test at best. But I wasn't able to recreate the issue in that case so far.

Curiously, when minimizing the app to the background after getting this error, it'll always get this error every time i resume the app from the background (which triggers a remote sync). I have to fully close the app and then open it again, which will usually then work. So to me it seems some cache shenanigans might also be at play.

Phone: iphone 15PM on ios 26.5
Server: Ubuntu 24.04 with nginx 1.30.0
immich 2.7.5 on both

Any help with this would be greatly appreciated, TIA!

elfin cragBOT
#

:wave: Hey @jovial crypt,

Thanks for reaching out to us. Please carefully read this message and follow the recommended actions. This will help us be more effective in our support effort and leave more time for building Immich immich.

References

#

Checklist

I have...

  1. :ballot_box_with_check: verified I'm on the latest release(note that mobile app releases may take some time).
  2. :ballot_box_with_check: read applicable release notes.
  3. :ballot_box_with_check: reviewed the FAQs for known issues.
  4. :ballot_box_with_check: reviewed Github for known issues.
  5. :ballot_box_with_check: tried accessing Immich via local ip (without a custom reverse proxy).
  6. :ballot_box_with_check: uploaded the relevant information (see below).
  7. :ballot_box_with_check: tried an incognito window, disabled extensions, cleared mobile app cache, logged out and back in, different browsers, etc. as applicable

(an item can be marked as "complete" by reacting with the appropriate number)

Information

In order to be able to effectively help you, we need you to provide clear information to show what the problem is. The exact details needed vary per case, but here is a list of things to consider:

  • Your docker-compose.yml and .env files.
  • Logs from all the containers and their status (see above).
  • All the troubleshooting steps you've tried so far.
  • Any recent changes you've made to Immich or your system.
  • Details about your system (both software/OS and hardware).
  • Details about your storage (filesystems, type of disks, output of commands like fdisk -l and df -h).
  • The version of the Immich server, mobile app, and other relevant pieces.
  • Any other information that you think might be relevant.

Please paste files and logs with proper code formatting, and especially avoid blurry screenshots.
Without the right information we can't work out what the problem is. Help us help you ;)

If this ticket can be closed you can use the /close command, and re-open it later if needed.

jovial crypt
#

I didn't provide server logs since no logs are produced when the issue occurs

elfin cragBOT
ripe solar
#

One thing to note about proxy logs: these often operate on HTTP level. This means when the TCP or TLS handshakes have problems, they won't show up in the proxy logs since it's still a stage before receiving actual HTTP data. Not sure if nginx can be made to log on tcp level.

Other than the usual "check if the proxy is properly returning the certificate chain" I don't really know what could be going on there. As the client seems to get some certificate, just not one it's happy with 🤔

jovial crypt
#

That’s a good point, I’ll try to dig into that a bit. I’m not super versed in nginx’s logging behaviour so I never thought of that

cursive thorn
#

do you use url switching?

jovial crypt
#

no, split dns (tho my new isp router supports NAT hairpinning so i technically don't need that anymore)

#

i've bumped up the error log level for nginx, and I see these logs when the issue happens:

2026/05/14 11:40:01 [info] 3202507#3202507: *335 peer closed connection in SSL handshake while SSL handshaking, client: 209.53.30.121, server: 0.0.0.0:443
2026/05/14 11:40:01 [info] 3202507#3202507: *341 peer closed connection in SSL handshake while SSL handshaking, client: 209.53.30.121, server: 0.0.0.0:443

and then i tried opening the app again after 2 mins without switching networks and I get this:

2026/05/14 11:41:57 [info] 3202506#3202506: *399 EVP_CipherFinal_ex failed while decrypting packet, client: 209.53.30.121, server: 0.0.0.0:443
2026/05/14 11:41:57 [info] 3202506#3202506: *404 EVP_CipherFinal_ex failed while decrypting packet, client: 209.53.30.121, server: 0.0.0.0:443
2026/05/14 11:41:57 [info] 3202507#3202507: *402 SSL_do_handshake() failed (SSL: error:0A000416:SSL routines::sslv3 alert certificate unknown:SSL alert number 46) while SSL handshaking, client: 209.53.30.121, server: 0.0.0.0:443

tbh this technical ssl stuff is a bit above my head so idk if that points towards anything at all or if i need to dig deeper

cursive thorn
#

this isn’t my wheelhouse either unfortunately. like we can see that it’s doing a new handshake when you switch networks and failing, but it succeeds when it’s starting on that network. maybe the handshake happens right as the dns switch happens and dns caching causes some issue? maybe?

jovial crypt
#

does the dns switch even matter given that the router does nat hairpinning, so even if it uses a cached or wrong entry with the public ip instead of local, shouldn't it still work? I'm gonna try forcing the phone to 1.1.1.1 for dns on local network too so it should always resolve to my external ip to remove that variable.

I also just had it happen that a good few minutes after the network switch, I opened the app and the remote sync worked fine but the "GET /users/me" call didn't (with the same cert error) and now I'm missing the server's storage space info. I tcp dumped the connection on the server and looked at it in wireshark; I don't see anything crazy from my limited knowledge, the handshake seems fine

pulsar hound
#

if you are using nat hairpinning definitely try the 1.1.1.1 DNS. DNS switching can be finnicky. NAT hairpinning performance concerns are wayyy overblown

jovial crypt
#

yeah, I was using it because my old router didn't have that feature, and I occasionally use it for ad blocking as well. but, I just tried it and still the same. interestingly the website on the same device loads fine (i tried a second or two before opening the app which failed again). I'm starting to think it might be something strange in the app/flutter/ios network handling. i wish i could give it a shot on android but i can't transfer my sim over

jovial crypt
#

So I think I fixed it... I previously had http3 set up, which I removed during debugging before making this thread. Since it didn't fix the issue I thought nothing of it. Well, I also had http3 set up for a different immich server on a separate subdomain on the same nginx instance, and disabling it THERE fixed the issue. I can only assume my device still had the http3 availability cached (I think I had a 1 day "max age" on the header so that makes sense) and kept trying to use it and failing without dropping to http2 for whatever reason. Very interesting that having http3 enabled on an unrelated subdomain and "server" block somehow can still cause issues.