mirror of
https://github.com/immich-app/immich.git
synced 2026-03-01 11:20:12 +03:00
[META] Experimental network features #5129
Open
opened 2026-02-05 11:10:12 +03:00 by OVERLORD
·
126 comments
No Branch/Tag Specified
main
renovate/npm-svelte-vulnerability
release/next
chore/translations
feat/notification
refactor/zod-migration
csp-policy
uhthomas/fix-mobile-video-state
feat/library-offline-stats
fix/top-bar-z-search
fix/video-zooming
feat/checksum-algorithm-indicator
feat/library-offline-count
uhthomas/feat-mobile-search-results
uhthomas/fix-mobile-hero-height
fix/bring-back-globalkeys
fix/map-webgl-error
visual-review/pr-26535
claude/auto-screenshot-web-changes-Y7efI
feat/mobile-ocr
feat/custom-date-range
fix/mobile-video-aspect-ratio
push-vxwxqoulmxun
push-zlzxxyywnmtr
push-mvnsqpxklmnu
push-ztrmyrpuwvow
push-pvvtwywwqzvy
fix/ml-ocr-batch-size
push-okmnxsumoyzr
push-lvyturrtwkrq
feat/mobile-edit-3-mobile-sync-handling
push-rsywxvptwxuv
push-snrprxmlposz
fix/timeline-rtl
feat/integrity-checks-izzy
uhthomas/fix-mobile-search-results
renovate/flutter
update-pwa
uhthomas/feat-sort-smart-search
renovate/github-cqlabs-homebrew-dcm-1.x
chore/deduplicate-storage-template-example
fix/maintenance-reload
feat/video-player
feat/mobile-editing
feat/use-native-clients
refactor/remove-replace-with-upload
uhthomas/chore-mobile-maplibre
uhthomas/mobile-fix-asset-details-album-pop
feat/crawl-wrapper
feat/open-in-browser
push-skvzqoozqkpl
feat/edit-filters
fix/locale-settings-desc
push-xyozownmuwqp
postgres-socketio
feat/pg-queue
proposal/zod
refactor/asset-upload
renovate/connectivity_plus-7.x
better-project-structure
uhthomas/mobile-feat-asset-viewer-details
fix/ml-rocm-build
fix/25803
feat/asset-file-apis
midzelis/wip
push-zpwsovysllvn
push-nwxlpmyzkyrl
feature/bottom-buttons-order
sqlite_thumbs
fix-keep-correct-ios-shared-album-asset
fix-memory-generation-and-display
push-vpxwmwwxwnvw
fix-migration-width-height
revert/prettier-translations
shared-deep-link-handler
feat/thumbnail-native-clients
feat/platform-clients
fix/foreground-cloud-sync
filter-by-person
feat/csp
refactor/sidebar
fix/disable-editing
fix/view-timeline-deeplink
image-zoom-on-slow-connection
fix/merged-edited-assets
open-api-fix
feat/create-job-with-dto
use-toast-primary
feat/vitest-4
feat/ios-fastlane-match
match-signing
fix-update-time-update-timeline
feat/modal-routes
feat/panorama-tiles
feature/mobile-view-asset-owner
feat/system-settings
feature/show-activity-count
better-info-in-asset-viewer
fix/all-people-count
feat/location-favorites
feature/rearrange-buttons-2
fix/download-storage-template
feat/kb-shortcuts-mobile
fix/people-count
push-qolzzzzxrvvn
chore/originals-in-asset-files
feat/asset-size-columns
ben/tree-a11y
new-search-filter-ui
refactor/expectSelectedReadonly
refactor/mobile-grdb
push-qvuktpxmkknu
feat/mobile-native-local-sync
refactor/timeline_ops
fix/scrubber_end
feat/version.txt
feat/context-menus
feat/server-chunked-uploads
refactor/virtualsegment
refactor/rename_daymonth_groups
fix/restrict-android-bg-worker
feat/android-periodic-worker
fix-remote-sync-clean-up
refactor/timeline_move_ops
fix/timeline_split_selectable
feat/keyboard_actions_help_modal
feat/static_frontend
feat/notification-warnign-android
feat/plugins2
feat/plugins
test/create-workflow-token-action
fix/docs-force
debug/search-result-similarity
debug/cf-chunked-uploads
feat/eslint_rule
feat/search-filter-album/web
refactor/timeline_photostream
refactor/timelineasset_asset
feat/session-permissions
feat/timeline_photostream_assetnav
feat/timeline_minor_optimize
feat/timeline_perf_nocomp
feat/timeline_search_results_actions
feat/timeline_search_results_page
fix/timeline_padding
fix/timeline_search_reactivity_warnings
feat/timeline_scrollbar
feat/timeline_stream_withviewer
fix/timeline_back_forth_nav
refactor/timeline_photostream_component
fix/generated-files-checks
fix/locate-button-local
chore/base-image-mimalloc
refactor/timeline_assetlayout
refactor/timeline_selectable
refactor/timeline_aware_actions
refactor/timeline_monthsegment
feat/remove-old-pages
chore/deps-gradle
tmp_photostream
tmp/lcms
feat/mobile-dynamic-thumbnails
fix/mobile-finer-thumbnail-concurrency
refactor/timeline1
refactor/extract_photostream
refactor/rename_load_api
refactor/timeline2
refactor/timeline3
feat/multi-select-asset-viewer
feat-no-thumbhash-cache
refactor/asset_grid
feat/faster-access-checks
fix/18991
fix/19543
chore/temp-remove
fix/21419
feat/mobile-hdr-images
chore/update-mise-lockfile
feat/mise-server-checks
feat/mise-ci
feat/windows-2025
feat/dev_cli
refactor/mobile-migrate-clients
fix/map-theme
fix/require-checkbox
chore/use_swc
feat/efficient-thumbnail-decoding
refactor/mobile-thumbhash
refactor/mobile-thumbhash-new
feat/beta-background-upload
fix/beta-timeline-memories-setting
fix/failed-uploads-not-removed
feat/mobile-shared-album
feat/groups
drift-map-page
drift-auth-user-sync
fix/disable-memory
feat/add-to-album-action
edit-date-time-action
drift-people-page
sqlite-remove-isIn
chore/required-reviewers
refact/asset-manager
fix/folder-sort
pnpm
feat/widget-multiple-server-urls
chore/medium-tests-dbname
fix/web-no-iterator-find
fix/map-pan-interruption
track-livephotos
timeline_events
chore/oxlint-migration
feat/maintenance-worker
feat/dav
chore/demo-snapshot
refactor/server-side-dedupe
feat/integrity-checks
dev/recognition-eval
lighter_buckets_test
perf/postgres-queue
postgres-queue
focus_rings
refactor/web-stores-1
refactor/add-to-taken
feat/sort-places
vet
tmp/demo-snapshot-preview
fix/server-migration-file-extension
fix/asset-update-race-condition
rknn-toolkit-lite2
refactor/mobile-split-up-search-page
feature/Add-rocm-support-for-machine-learning
feat/rocm
chore/async-hash-file
feat/shared-link-view-count
feat/rotation
feat/graphql
feat/job-ids
feat/ignore-library-permission-error
feat/docker-compose-builder
feat/kysely-typeorm
mobile/onboarding
no-video-player
fix/server-qsv-output-format
chore/server-geodata-tweaks
mobile/native-video-player-no-hero
feat/xxhash
fix/docs-concurrency
feat/local-tileserver
refactor/exif-orientation
original-path-infix
refactor/mobile/login-form-1
feat/server-editor-endpoints
fix/server-qsv-vbr
fix-mobile-db-problems
feat/ml-armnn-conversion
feat/mobile/backup-with-album-info
feat/fast-initial-sync-1
chore/handle-output_dims
feat/unassign-faces
feat/shortcuts-on-asset-grid
feat/capacitor-mobile-app-poc
feat/server-nvenc-hw-decoding
fix/mobile-fetch-non-archive
web/automation-ui
feat/mobile-server-endpoint-save-dropdown
object-storage
feat/memories-animations
dev/metrics
ml/tflite
feat/ml-export-cli
v2.5.6
v2.5.5
v2.5.4
v2.5.3
v2.5.2
v2.5.1
v2.5.0
v2.4.1
v2.4.0
v2.3.1
v2.3.0
v2.2.3
v2.2.2
v2.2.1
v2.2.0
v2.1.0
v2.0.1
v2.0.0
v1.144.1
v1.144.0
v1.143.1
v1.143.0
v1.142.1
v1.142.0
v1.141.1
v1.141.0
v1.140.1
v1.140.0
v1.139.4
v1.139.3
v1.139.2
v1.139.1
v1.139.0
v1.138.1
v1.138.0
v1.137.3
v1.137.2
v1.137.1
v1.137.0
v1.136.0
v1.135.3
v1.135.2
v1.135.1
v1.135.0
v1.134.0
v1.133.1
v1.133.0
v1.132.3
v1.132.2
v1.132.1
v1.132.0
v1.131.3
v1.131.2
v1.131.1
v1.131.0
v1.130.3
v1.130.2
v1.130.1
v1.130.0
v1.129.0
v1.128.0
v1.127.0
v1.126.1
v1.126.0
v1.125.7
v1.125.6
v1.125.5
v1.125.4
v1.125.3
v1.125.2
v1.125.1
v1.125.0
v1.124.2
v1.124.1
v1.124.0
v1.123.0
v1.122.3
v1.122.2
v1.122.1
v1.122.0
v1.121.0
v1.120.2
v1.120.1
v1.120.0
v1.119.1
v1.119.0
v1.118.2
v1.118.1
v1.118.0
v1.117.0
v1.116.2
v1.116.1
v1.116.0
v1.115.0
v1.114.0
v1.113.1
v1.113.0
v1.112.1
v1.112.0
v1.111.0
v1.110.0
v1.109.2
v1.109.1
v1.109.0
v1.108.0
v1.107.2
v1.107.1
v1.107.0
v1.106.4
v1.106.3
v1.106.2
v1.106.1
v1.106.0
v1.105.1
v1.105.0
v1.104.0
v1.103.1
v1.103.0
v1.102.3
v1.102.2
v1.102.1
v1.102.0
v1.101.0
v1.100.0
v1.99.0
v1.98.2
v1.98.1
v1.98.0
v1.97.0
v1.96.0
v1.95.1
v1.95.0
v1.94.1
v1.94.0
v1.93.3
v1.93.2
v1.93.1
v1.93.0
v1.92.1
v1.92.0
v1.91.4
v1.91.3
v1.91.2
v1.91.1
v1.91.0
v1.90.2
v1.90.1
v1.90.0
v1.89.0
v1.88.2
v1.88.1
v1.88.0
v1.87.0
v1.86.0
v1.85.0
v1.84.0
v1.83.0
v1.82.1
v1.82.0
v1.81.1
v1.81.0
v1.80.0
v1.79.1
v1.79.0
v1.78.1
v1.78.0
v1.77.0
v1.76.1
v1.76.0
v1.75.2
v1.75.1
v1.75.0
v1.74.0
v1.73.0
v1.72.2
v1.72.1
v1.72.0
v1.71.0
v1.70.0
v1.69.0
v1.68.0
v1.67.2
v1.67.1
v1.67.0
v1.66.1
v1.66.0
v1.65.0
v1.64.0
v1.63.2
v1.63.1
v1.63.0
v1.62.1
v1.62.0
v1.61.0
v1.60.0
v1.59.1
v1.59.0
v1.58.0
v1.57.1
v1.57.0
v1.56.2
v1.56.1
v1.56.0
v1.55.1
v1.55.0
v1.54.1
v1.54.0
v1.53.0
v1.52.1
v1.52.0
v1.51.2
v1.51.1
v1.51.0
v1.50.1
v1.50.0
v1.49.0
v1.48.1
v1.48.0
v1.47.3
v1.47.2
v1.47.1
v1.47.0
v1.46.1
v1.46.0
v1.45.0
v1.44.0
v1.43.1
v1.43.0
v1.42.0_65-dev
v1.41.1_64-dev
v1.41.0_64-dev
v1.40.1_63-dev
v1.40.0_63-dev
v1.39.0_61-dev
v1.38.2_60-dev
v1.38.1_60-dev
v1.38.0_60-dev
v1.37.0_58-dev
v1.36.2_56-dev
v1.36.1_55-dev
v1.36.0_55-dev
v1.35.0_54-dev
v1.34.0_53-dev
v1.33.1_52-dev
v1.33.0_52-dev
v1.32.1_51-dev
v1.32.0_50-dev
v1.31.1_49-dev
v1.31.0_49-dev
v1.30.2_48-dev
v1.30.0_46-dev
v1.29.6_45-dev
v1.29.6_44-dev
v1.29.5_44-dev
v1.29.4_44-dev
v1.29.3_43-dev
v1.29.2_43-dev
v1.29.1_43-dev
v1.29.0_42-dev
v1.28.4_41-dev
v1.28.4_42-dev
v1.28.3_41-dev
v1.28.2_40-dev
v1.28.1_39-dev
v1.28.0_38-dev
v1.27.0_37-dev
v1.26.0_36-dev
v1.25.0_35-dev
v1.24.0_34-dev
v1.23.0_33-dev
v1.22.0_32-dev
v1.21.1_31-dev
v1.21.0_31-dev
v1.20.3_30-dev
v1.20.2_30-dev
v1.20.1_30-dev
v1.20.0_30-dev
v1.19.1_29-dev
v1.19.0_29-dev
v1.18.0_27-dev
v1.17.0_25-dev
v1.16.0_23-dev
v1.15.1_21-dev
v1.15.0_21-dev
v1.14.0_21-dev
v1.13.0_20-dev
v1.12.0_18-dev
v1.11.0_17-dev
v1.10.0_15-dev
v1.9.1_14-dev
v1.9.0_13-dev
v1.8.0_12-dev
v1.7.0_11-dev
v1.6.0_10-dev
v1.5.1+9-dev
v1.5.0+8-dev
v1.4.0+7-dev
v1.4.0+6-dev
v1.4.0-dev
v1.3.0-dev
v1.3.1-dev
v0.6-dev
v0.5-dev
v0.4-dev
v0.3-dev
v0.2-dev
first-android-release
Labels
Clear labels
accessibility
changelog:enhancement
changelog:security
changelog:skip
changelog:translation
cli
date-time
dependencies
documentation
external-library
format
good first issue
mobile-beta
mobile-beta
mobile-beta
needs-answer
nice to have
pull-request
sharing
tech-debt
📱mobile
🖥️web
🗄️server
🧠machine-learning
Mirrored from GitHub Pull Request
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: immich-app/immich#5129
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @mmomjian on GitHub (Jan 10, 2025).
Experimental network features
Features
There are some network features that have been added to the Immich mobile app through the years that interact with various libraries and background tasks in unexpected ways. These features include:
Limitations
Typically, these network features work fine in the normal library view, but cause issues with:
Solutions
Thanks to the recent implementation of auto-switching server URLs in #14437, many of these features are no longer needed for a smooth Immich experience. There are other options, including the use of a VPN or a real SSL cert.
Handling known issues
We have updated the FAQ (link will work once #15228 is merged). This issue can be used to track any known issues with these experimental network features. These issues will not be a priority for the dev team, but can be improved by the community if possible.
Please do not use this discussion for comments like "same" or +1". This is designed only to keep track of current known limitations to these experimental features, or discuss potential solutions and future patches.
@rovo89 commented on GitHub (Jan 10, 2025):
The final sentences sound quite intimidating, so I'm wondering: Is it allowed to discuss analysis of root causes and potential solutions here? Or where would that go?
@mmomjian commented on GitHub (Jan 10, 2025):
If the discussion is relevant to potential future PRs or improvements, or the actual underlying issue, that's fine. Often these topics turn into people repetitively saying "I want this feature too", that is what we are trying to avoid
@ckuyehar commented on GitHub (Jan 10, 2025):
I propose that we use the discussions area to discuss a specific feature. If you start a discussion, mention it in the discussion and ask @mmomjian to update the top comment so that it links to the discussion.
The moment, two or more features are discussed in this single thread, it's going to get chaotic.
@bo0tzz commented on GitHub (Jan 10, 2025):
It is already know that these features do not work well, so we should avoid rehashing "it's broken" over and over again.
I would propose instead to keep this thread focussed on very specific discussion of why things are not working (eg referencing specific code paths and such) that might result in a PR. That way things should not get chaotic, and there should be no need for more separate issues or discussions (which is chaotic in its own way).
@rovo89 commented on GitHub (Jan 10, 2025):
As per https://github.com/immich-app/immich/issues/14845#issuecomment-2584007919, "app crashes" should be added to the list of limitations. Happens on Android whenever I navigate to a video, e.g. by clicking on it in the timeline or when swiping through it. I don't even need to click the "play" button to provoke the crash.
@rovo89 commented on GitHub (Jan 10, 2025):
As far as analysis is concerned, I'm copying my findings from https://github.com/immich-app/immich/issues/5553#issuecomment-2582523158 which is closed meanwhile. My setup uses mTLS (client certificates) and a Let's Encrypt server certificate, I'm using Android.
A major problem seems to be the many levels of abstraction and different packages used. Flutter (using Dart), Jetpack, Android framework... that make it hard to follow the call chain and pass the required information down to the places where connections are actually used.
For most parts of the app, Immich uses HttpSSLCertOverride to set the client certificate and allow self-signed certificates (for the Immich server host only), which is set globally here. But I think that only applies to places where Dart's standard HttpClient class is used. Under the hood, I think it makes adjustments to OpenSSL's SSLCertContext which is wrapped by Dart's SecurityContext. My understanding is that the native HTTP client isn't involved at all here, but OpenSSL is used directly.
The video player seems to use the Android native HTTP client which is created here. DefaultHttpDataSource comes from here and uses HttpURLConnection. IIUC, Android uses a customized OpenJDK with their own handlers, in this case com.android.okhttp.HttpsHandler. This seems to be the place where they set SSL options, which uses HttpsURLConnection.getDefaultSSLSocketFactory(). I haven't tracked it down completely yet, but I think SSLContext.init() is where a client certificate would be specified.
I have just written this down to understand the current flow and maybe get corrections from someone. Next step would be finding a way to pass the client certificate to the appropriate place without breaking abstraction... Maybe it would be sufficient to set call HttpsURLConnection.setDefaultSSLSocketFactory() in Immich code?
For completeness, the download library has some code to accept untrusted certificates which calls exactly that method with an accept-all trust manager, indicating that it could profit from this approach as well.
@ckuyehar commented on GitHub (Jan 11, 2025):
Custom HTTP proxy headers
Can someone refer an issue related to this one?
@mmomjian commented on GitHub (Jan 11, 2025):
It's possible there's no known issues. However we still consider it an experimental feature. Glad it's working well!
@rovo89 commented on GitHub (Jan 11, 2025):
As for the crash in the video player, here is the trace with a debug build:
So it crashes here because HttpDataSource.InvalidResponseCodeException is a
java.lang.Exception, not ajava.lang.Error. The common super-class for both isjava.lang.Throwable, and indeed changing the cast and the onError() parameter to Throwable solves the crash. The throbber (animated circle spinning while loading) is simply shown indefinitely and I can navigate to other images.I'll send a PR for that to
both upstream andthe Immich fork.EDIT: It's only in the code that was added to move to Exoplayer.
@ckuyehar commented on GitHub (Jan 11, 2025):
The background_downloader does NOT allow self-signed certificates in release mode. I previously mentioned this in #15188.
However... even if you were to trust your CA and import your client certificate in your mobile device, the background_downloader will still fail. Why? The package background_downloader v8.9.0 doesn't support mutual TLS.
@rovo89 commented on GitHub (Jan 11, 2025):
I wasn't planning to use their debug feature. I mentioned it because it shows that their implementation seems to consider whatever is dictated by HttpsURLConnection.setDefaultSSLSocketFactory(). Calling the latter from Immich code should therefore have the same effect, and I have hopes that this approach would also allow to pass client certificates. See KeyManager in SSLContext.init().
@rovo89 commented on GitHub (Jan 11, 2025):
Yeah, my proof of concept for Android is working. 🥳 I added the following in ImmichApp.kt:
I successfully tested videos and image downloads. That's for mTLS only. I see no problem accepting any server certificate, the question is whether that would need to be restricted to only the Immich server host like here. But maybe that could be done by checking the hostnames in the certificate... not sure.
Any comments on whether such an approach would be acceptable? Of course, the imported key would need to be used instead of assets (was just easier for testing) and it would have to be reloaded when the key is changed. Also no idea how I can create a dedicate native "component" for this stuff, I might need some assistance with that once the general idea has been confirmed. And sorry, no idea about iOS.
@ckuyehar commented on GitHub (Jan 11, 2025):
🥳 congrats! awesome job.
Immich should get out of the business of making that decision and defer that to Android/iOS. Let the OS perform the necessary checks.
I do have some thoughts... Instead of maintaining/creating another certificate store within the Immich mobile app (abbrev IMA)... IMA should select the certificate from the OS. This will ensure we're using the OS certificate stores (trusted CAs and user) instead of a custom store within IMA. This will also allow IMA (when the time is right) to depreciate "allow self-signed certificates" because the CA would be trusted on the mobile device and a "self-signed" error would never appear.
In other words you should be able to trust the CA, uncheck "allow self-signed certificates" in IMA, select your client certificate in the Android User Store and login to IMA, download images and play videos without issue.
@mertalev commented on GitHub (Jan 11, 2025):
cc @etnoy if you have any thoughts on this
@bo0tzz commented on GitHub (Jan 11, 2025):
Flutter has its own certificate handling and doesn't use the system's. That's the whole reason we're here in the first place.
@rovo89 commented on GitHub (Jan 11, 2025):
Exactly. It's neither my idea nor my preferred approach to upload the certificate in the Immich app, I'm just following the path that was already taken.
@rovo89 commented on GitHub (Jan 11, 2025):
https://github.com/dart-lang/sdk/issues/50435 sounds like no real solution is planned for the HTTP client in Dart, they suggest using
package:cronet_http. No idea about that, I'm not familiar with Dart. Indeed it feels a bit clunky having to upload the certificates again that I already configured in the OS, but it's a one-time thing, so it's acceptable for me as a user with a special setup.I also thought about adding the self-signed certificate to the trust store, which I would anyway do in the OS, but using it from there has the same limitations as above and I'm not sure if yet another upload button in Immich would be worth it. So the simple switch sounds like an acceptable compromise to me.
@LordArrin commented on GitHub (Jan 29, 2025):
Right now, the system looks like so overloaded with all of it security stuff, it's no wonder something breaks. I'll join the commentators above, all apps on device MUST trust system CA, it just built for it. Some strange decisions like SSL Pinning can be acceptable in banking or payment apps, but not in userspace.
@LordArrin commented on GitHub (Jan 29, 2025):
And yep, I use Immich behind nginx with my personal CA's, and literally all works amasing, but not a video playback and downloading images from Immich on Android. From desktop browsers, android browsers all of it works like charm.
@rovo89 commented on GitHub (Jan 29, 2025):
While I generally agree that apps should use the system SSL stack, the reason why this is not the case is in the platform abstraction of Dart, not in the immich app itself. As document above, Dart doesn't use the Android HTTPS client, but rolls its own based on OpenSSL directly.
I don't think switching away from Dart is an option. 😉 There are some ideas to add support for private keys from the Android keystore (which can't be accessed as bytes, the system will take care of encryption): https://github.com/dart-lang/http/issues/1237. Or it has been mentioned in https://github.com/dart-lang/sdk/issues/50435 to use
package:cronet_httpinstead.But all of these options are either just ideas/wishes without any commitment and timeline, or they would have a bigger impact on the app itself. As it has been made clear that use-cases such as mTLS have low priority, I can't imagine that we'll see such changes anytime soon.
On the other hand, the Immich app does already have options to upload the client certificate. Yes, it's redundant, but for me as a user/admin, that's much better than other workarounds I'd have to implement in the current state (e.g. add another domain without mTLS), or not being able to watch remote videos at all.
The approach I mentioned in https://github.com/immich-app/immich/issues/15230#issuecomment-2584938841 is fairly low impact, not much code (= less to maintain) and doesn't add any further user-facing workarounds. It also matches the semantics of the existing codes to globally enable the client certificate - just for a different HTTP client.
That's why I'd still like to work on implementing this properly, and I hope to get some agreement from the core devs that such a PR would be merged once it's ready. Can I get that?
@LordArrin commented on GitHub (Jan 29, 2025):
Find a temporary solution. I enabled LSPosed module SSLUnpinning for Immich, relaunch app and all videos plays like it may do.
@rovo89 commented on GitHub (Jan 29, 2025):
Interesting to see that variants of Xposed are still around, I'm the original author 😉 But nowadays I don't even root my phone anymore, which is the prerequisite.
Besides that, this method might help to disable SSL validation globally (which is a very big hammer), but I don't see how it would make mTLS possible. The private key of the client certificate should be stored in separate hardware, unaccessible even for the Android core, and encryption takes place via IPC as far as I understood.
@etnoy commented on GitHub (Jan 29, 2025):
I have nothing to contribute to this issue, but I just want to thank you for Xposed. It's been a few years but I remember those wild days fondly :)
@polomani commented on GitHub (Feb 5, 2025):
@rovo89
Having multiple domains for the app might not work as in that case you'd need to work around the authentication (at least for the web version, it uses cookies, which aren't easily shared between domains).
After some trial and error, I solved it on my setup using HAProxy (it is most likely possible with other reverse proxies, but I needed something with low CPU/RAM consumption).
Here I first define
verify optionalwhich will validate the client certificate only if it is available.Later in the code it checks the URL path, and lets the request through without the client cert only for the video playback endpoints.
Basically, mTLS is required on everything else, except the problematic video playback endpoint.
haproxy.cfg:
This is slightly not ideal, but if there is no proper fix, allows us to keep using mTLS setup and all is behind one domain / ip.
IMHO mutual TLS is a robust, secure, and reliable way for people to expose their services to the internet and should be a recommended approach. Much better then compared to relying on VPNs like Tailscale (as then it can't even be considered as pure self-hosting anymore :) ).
@rovo89 commented on GitHub (Feb 28, 2025):
I drafted PR #16403 to support remote video playback and asset downloads with mTLS. Android-only, not sure if this problem even exists on iOS. It shouldn't be too hard to add support for self-signed certificates, but I want to get some feedback first.
@pedropombeiro commented on GitHub (Feb 28, 2025):
@rovo89 I can confirm that the problem also affects iOS.
@shenlong-tanwen commented on GitHub (Mar 3, 2025):
The changes required for
cronet_httpshould be rather simple. We should test it a lot to make sure it is stable and that nothing breaks. However, AFAIK, using it will solve the issue only with the network calls made from dart but not the one made from the native side like the video player or the background downloads.@rovo89 commented on GitHub (Mar 4, 2025):
Now that you say it, that makes sense... In other words, using
cronet_httpmight make the current workarounds more elegant, but it won't solve the open issues."More elegant" means that self-signed certificates could be imported into the Android trust store and would be automatically accepted. Client certificates could be imported into the Android keystore, however an app must bring up the dialog to choose the key and grant permission to it. So some special code would still be required, and I'm not sure whether all required interfaces are exposed to Dart. I assume that additional code would be required for the video player and downloads.
I have no idea which timeline would be realistic for a switch - if it happens at all. So from my point of view, it make sense to move forward with the PR. I'm currently trying to enhance it for self-signed certificates. The main difficulty is to accept them only for the Immich host...
@Trezamere commented on GitHub (Mar 9, 2025):
This isn't really an acceptable solution, it needs to allow CAs, rotating your client certs regularly is a basic and common best practice and Caddy (and others) do it for free. I've had to "Allow self-signed SSL certificates" which is not ideal to even connect to the server, it's strange that even with that though you still can't download remote assets, it should just be ignoring it.
@rovo89 commented on GitHub (Mar 10, 2025):
Are you sure you're talking about client certificates here? Everything in your comment sounds like you mean server certificates instead. The former is for authentication, proofing that you are a certain user (comparable to a password), whereas the latter is how the server proofs that it's the one you wanted to talk to and not some man-in-the-middle which intercepted your traffic.
The import button is only meant for client certificates. If you upload a server certificate instead, you might not get an error because the format is the same. But it won't help anything because the certificate will only be used when the server asks for it, and it won't be used to check the server's authenticity. The "Allow self-signed SSL certificates" on the other hand is only for server certificates, because obviously there can't be a toggle in the app to tell the server to trust your client certificate.
As you mention best practices - the much better way would be getting a domain and using Let's Encrypt to get a valid, trusted server certificate. That can even work for installations in your home network because you can proof ownership via DNS, in which case you can also get a wildcard certificate for your whole domain (*.example.org).
Exactly that issue would be solved on Android with #16403, for both client certificates and self-signed server certificates.
@Trezamere commented on GitHub (Mar 10, 2025):
Ahh, you're right, in my frustration trying to get this working I overlooked that it was specifically a client certificate (you even said as much 🤦). I assumed it was for uploading a server cert as it's unusual to see built-in client certs/mTLS in these hobby projects IME.
While certainly convenient, I think it's a stretch to say using a public CA is a 'best practice'; there are many use-cases for leveraging self-signed certs. Undoubtedly wildcard certificates are not included in best practices.
In any event thank you for putting that MR together and hopefully that can get merged soon™, will keep an eye on it.
@Fmstrat commented on GitHub (Apr 28, 2025):
@rovo89 I was directed here, then read through the chat in the above PR, and wondering if you could provide some clarity for me since I'm apparently just dumb today.
Given the comment about self-signed server certificates, does this mean 16403 would allow for importing a CA and mTLS, or just mTLS?
Because for us...
This isn't best practice for all threat models. Public CAs are not "trusted", but an internal enterprise one, is.
@rovo89 commented on GitHub (Apr 28, 2025):
I didn't add any new settings, my PR "only" applies the existing settings to the remote video player and media downloads on Android, i.e. those parts which don't use Dart's HTTP wrapper.
The existing settings allow you to upload a client certificate for mTLS and you have a switch to trust all server certificates. I think the hostname must still match and probably validity date as well.
I understand that there might be scenarios where that big hammer is too much, but uploading a trusted server CA certificate is out of scope for my PR. If someone works on that, it will certainly be easier for them to apply this to non-Dart after my PR has been merged.
By the way, I made the comment about best practices after someone suggested that it's common to regularly rotate client certificates and that the app should have a way to handle that. To me, that sounded much less common than someone using Let's Encrypt to get a server certificate for their publicly reachable reverse proxy. "Best" practice is probably not the right word, as it depends on the individual situation, so read it as "which setup fits for many people".
@Fmstrat commented on GitHub (Apr 29, 2025):
@rovo89 Thanks for the feedback, that helps. Could we instead make Immich just trust the CAs that are installed on the Android device? For instance,
ntfydoes this by adding this line to Android Manifest:31eadd9768/app/src/main/AndroidManifest.xml (L31)And then a network config file of: https://github.com/binwiederhier/ntfy-android/blob/main/app/src/main/res/xml/network_security_config.xml
This file is what allows user-defined CAs to be validated in the app. A few lines of XML changes may be all that's required.
Note,
cleartextTrafficPermittedin that network config just means HTTP can be used alongside HTTPS (default is both) as thattruecan come across as a scary setting.@rovo89 commented on GitHub (Apr 29, 2025):
... if Dart was using the Android HTTP wrapper. But it isn't, it comes with its own wrapper which uses OpenSSL directly. I did quite some analysis before the PR which you can read in https://github.com/immich-app/immich/issues/15230#issuecomment-2584057446, although it was for the other way around.
For the Android "native" implementation that I added, such configuration might be enough, but for the 95% of the app which use Dart, it would mean having to replace the whole HTTP stack.
@vekexasia commented on GitHub (May 20, 2025):
As reported in #18396, after setting up mtls certificate and verify that it is working properly i cannot configure the automatic url switching cause the external url gives me a red exclamation mark.
@amigthea commented on GitHub (May 22, 2025):
unfortunately, even with the new pr, having both server and app on v1.133 does not resolve the video playback on mTLS for me
can I provide some logs or what to help troubleshooting?
@rovo89 commented on GitHub (May 22, 2025):
Are you on Android and mTLS works for photos? You can try to provide logs (logcat probably), maybe they contain something strange. You don't happen to have something like this in the Immich logs? https://github.com/immich-app/immich/pull/16403#issuecomment-2779820363
@amigthea commented on GitHub (May 22, 2025):
I'm on android and mTLS did not give me problems with photos, even before the 1.133 fix.
this are the immich app log on mTLS, the https://github.com/immich-app/immich/pull/16403#issuecomment-2779820363 error does not shows up but there's another one
when I'm trying to play video it remains stuck on a frame, with the immich loading icon spinning in the center.
I'm doing something wrong?
@amonhk commented on GitHub (May 22, 2025):
I also have the same problem with mTLS (self-signed) using the latest Android client and server (v1.133).
When on-device content is deleted, downloads of images and videos from the server fail.
(server uses a custom port, not 443)
@rovo89 commented on GitHub (May 22, 2025):
The custom port is an interesting detail, although as far as I can see only the hostname is checked (as it should be). Could you check if
adb logcatshows anything unexpected?@amigthea commented on GitHub (May 22, 2025):
right know I cannot use
adb logcatbecause I use mTLS only for public connection and it's bypassed for LAN connection. If no one else can provide them I will bring up a test environment asap@amigthea commented on GitHub (May 22, 2025):
I was able to find a faster workaround, here's the logcat
The log block starts from when I click to play video from its timeline thumbnail and seems to stop logging at the loading icon loop, is this sufficient?
@rovo89 commented on GitHub (May 23, 2025):
HTTP status code 418 would mean I'm a teapot?!? Sounds very strange... Is your server on a non-standard port as well?
@amonhk Do you see the same error in your logcat? Do you maybe have a similar phone? From past experience I know that vendors sometimes bring their own implementations of even basic stuff. Although the APIs used (especially
HttpsURLConnection.setDefaultSSLSocketFactory()) should be, well, public APIs, which are usually covered by CTS tests, so less freedom for vendors to change the behavior.You could also try downloading an image that doesn't exist locally, it uses the same Android-native HTTP client, but with less indirections.
@amigthea commented on GitHub (May 23, 2025):
you gave me a really good morning laugh, my bad I forgot to specify that my proxy masks the http code 496 to 418
I'm on standard 443
I'm on a oneplus 9 pro
@amigthea commented on GitHub (May 23, 2025):
eureka! I solved by logging out from the app, clear its cache and login again on mTLS; now video playback it's flawless, thank for the effort <3
@amonhk commented on GitHub (May 23, 2025):
I hope I have managed to extract the useful parts of the log.
This is when I open a video that is not present on the device, and the player attempts to play it
This should be the point where I try to download the content.
It seems that the certificate entered in the app is not being used?
I'm on a Find X5 Pro
@amonhk commented on GitHub (May 23, 2025):
I just tried but without success...
Did you import the certificate both in the app and in the android os? self-signed certificate?
@amigthea commented on GitHub (May 23, 2025):
before v1.133 I had import the client certificate both on android and in immich, but in the recent login attempt that solved the problem I noticed how I could not import the certificate in Immich due to both
ImportandRemovebutton onSSL Client Certificateoption greyed out; @rovo89 do the implemented bugfix directly use the android certificate store?I'm using a mTLS certificate signed by a self-signed CA
@rovo89 commented on GitHub (May 23, 2025):
I think you can only change these options (and allow self-signed server certificates) before you log in.
No, those certificates can't simply be used, apps need to go through a selection and approval workflow. The uploaded certificate is used to create a new
SSLContext, which is then set asHttpsURLConnection.setDefaultSSLSocketFactory():529359de2d/mobile/android/app/src/main/kotlin/app/alextran/immich/HttpSSLOptionsPlugin.kt (L55-L76)The best explanation I have so far is that some vendors have an alternative HTTPS client which ignores the default SSLSocketFactory. Very strange that it worked for you after a fresh login and clearing the cache, as the setup should be done in the running process only.
@amigthea commented on GitHub (May 23, 2025):
that's the first thing I tried but was already greyed out at that stage, I will uninstall the app completely and try again asap, to repeat those steps in a clean context and give a more reliable feedback
update, I can confirm it asks for the certificate only on first mTLS login. The step i took are:
@amonhk commented on GitHub (May 24, 2025):
Great! I managed to solve the issue—the problem was with the certificate.
This post was very helpful:
https://android.stackexchange.com/questions/252812/how-to-get-chrome-or-any-browser-to-present-a-tls-client-certificate
If the certificate chain is missing intermediate certificates, the issue only affects remote content, while both the app and the browser (Firefox) didn't show any problems.
For now, I've installed the client certificate both in the operating system and in the app, and everything is working fine.
Thank you @rovo89 for this implementation; I've been looking forward to it for a long time!
@marsara9 commented on GitHub (May 27, 2025):
I tried a similar set of steps but video playback only works once.
If I then redo the steps above from the beginning everything works. So while I can get playback to work it requires that I physically sign out after each session. As it doesn't look like it's saving the certificate on each app launch.
@amigthea commented on GitHub (May 27, 2025):
I'm sorry to hear that, it's not something I'm currently facing; tried both killing it (once logged) e rebooting my phone but remote playback still works, certificate is correctly loaded and I even successfully shared one videos on another app
@wiedaar commented on GitHub (Jun 10, 2025):
video's are playing, the only thing that doesn't work is downloading, i'm using caddy for windows. The workaround we are using is login in via the webbrowser on android, only if you want to download something.
@jamesonuk commented on GitHub (Jun 11, 2025):
I did raise https://github.com/immich-app/immich/issues/19115 which has been closed to this which my search skills failed to locate.
I do sysadmin work and
SSLContextis a way of specifying trust and identity. Java I can do but Kotlin always confuses me.... That codec03e72c1da/mobile/android/app/src/main/kotlin/app/alextran/immich/HttpSSLOptionsPlugin.kt (L55-L58)looks like it essentially uses null as the
TrustManagerthis has caused me issues previously and I have ended up loading a CA from creating theTrustManagerfrom that which works fine.I don't know Android but I know that Home Assistant recognises my local CA (which I have added to Android) and they load
TrustManagerarray slightly differentlya51b398953/common/src/main/kotlin/io/homeassistant/companion/android/common/data/TLSHelper.kt (L24-L26)Not sure whether this is down to the use of Dart here or something else but HA certainly seems to be able to make use of system certs (is that a case of they have been certified by Google to do so?). Enterprise Java I can dabble in, Android and Kotlin might as well be Klingon... Just throwing out some thoughts
@wiedaar commented on GitHub (Jun 11, 2025):
i have no idea what happened but today we can download again, i've changed nothing.
@rovo89 commented on GitHub (Jun 12, 2025):
Yes. Because the code you linked to is only used in a few scenarios like playing videos which aren't available locally or downloading media. The majority of network communication is done via Dart, and they use their own HTTP/SSL implementation based on OpenSSL directly. I did some investigations in https://github.com/immich-app/immich/issues/15230#issuecomment-2584057446, but it goes through a lot of layers... My understanding is that the Dart implementation is so generic that it doesn't use the Android (or iOS) trust store, and therefore doesn't know about certs you added to it. For mTLS it's even more tricky because the certificate can't be retrieved from the secure store, instead the Android code uses an opaque object for the key manager, and when it comes to signing some bytes, that is handed over to the system.
@Tupsi commented on GitHub (Sep 2, 2025):
I am confused. My ticket #21520 got closed saying it is a duplicate of this. But this here states contradicting information.
First it says, there is an experimental feature (which I can not find anywhere in the app)
Manually imported root CA / SSL certificate
then it says under "Solution" that it is no longer needed because of the new feature from #14437. My issue is exactly describing that this does not work with installed root CAs.
There is also a 2nd slightly misleading part in the article, when you combine the proxy headers with a solution of "use a vpn". Just want to point out, that I just instaled pangolin as vpn service and was very glad to see, that you have an option to enter exactly such proxy headers, so I get around the auth process in pangolin. So please do not remove that!
@Fmstrat commented on GitHub (Sep 2, 2025):
@Tupsi skim the issue looking for my username and you'll see why. The dual URL feature has no impact on CA.
It seems Immich uses a special SSL stack to support (IMO a really weird) cert-per-device use case, ignoring the system CA. I'm going to guess a primary Dev uses this setup, which is why it hasn't been shifted to just standard libraries that use the standard custom CA method.
Comments from devs above make ot seem like they think it's an issue with Dart libraries, but every Dart application I've contributed to has zero issues with custom CAs, so I'm unclear on if there will ever be a solution.
@rovo89 commented on GitHub (Sep 2, 2025):
With the cert-per-device, is it possible that you mean client certificates aka. mTLS? If yes, that's a rather niche use-case of an additional authentication layer, but it's completely independent from the more common use-case of a self-signed server certificate. I helped adding fixes/workarounds for the former, but there were already some in place for the latter.
I had looked through the source code of Dart back then and indeed found signs that it doesn't use Android's certificate store but brings its own set of trusted certificates, which obviously doesn't include the ones uploaded by the user. There were lots of GitHub issues around this, but I don't think any solution.
Is there any small sample project which uses Dart's standard HttpClient and is able to connect to an HTTPS server with a self-signed certificate that has been uploaded to Android?
@Tupsi commented on GitHub (Sep 2, 2025):
@rovo89 you should stop using the term "self signed" as my case is not self signed. Having your own root CA as trust anchor is a total valid use case in a scenario where you either do not trust the other parties or do not have the ability to connect to said parties over your network.
@Fmstrat
thanks for your explanation, helps me giving up on the whole subject to find a solution (as there simply is none as long as the app uses its own set of trusted CAs). I will just use only use my external connection url from no one for everything. With my internet connection I can live with the speed impact which is included in the decision.
My initial idea of writing the bug report was just that; reporting something that is outside of the expected way the app acts.
If I go out of my way to create my own root CA, create certificates which are in a chain with that and then upload/install the public part of that root CA to my device my expectation is that any warnings/error go away, because this is now a valid certificate in the eyes of the OS.
I understand now, that this is in the "will not fix" category of the developer, which I can accept.
@rovo89 commented on GitHub (Sep 2, 2025):
You're right, that was a bit of a simplification and mainly meant to differentiate from mTLS. Self-signed certs are probably what most people manage to set up though, if they want to setup HTTPS by themselves, without Let's Encrypt etc. In the end, the handling in the app isn't different, you need to get the CA into the trust store.
Yep, I would also expect that, and would have loved to take an easy solution, but as far as I can see, the app does use the standard framework that Flutter/Dart offers, which I believe doesn't handle this case (in contrast to native Android apps). So the sentiment of "will not fix" (as in "they don't want to") isn't right, at least I couldn't think of a way to make it work. If somebody wants to prove otherwise with a minimal app, I'm pretty sure it would be considered. Until then, the toggle to disable certificate checks might make the errors go away, but is the opposite of what you want to achive.
@Tupsi commented on GitHub (Sep 2, 2025):
aye, sorry if that came across (with the wont fix) tag. It wasnt ment as a slight, just a fact as I understood it. If, for whatever reason, the framework at some point in the future decides to use system certs instead of its on set it will just start to work. That I understood. As long, as the dev here uses the framweork he is bound by what the framework dictates. In the end I got my answer and can configure the app so that it works and that is all that counts for me in the end, so all good.
I am glad the whole immich project exists and does what it does, so thank you for this!
@Fmstrat commented on GitHub (Sep 2, 2025):
@rovo89 Oh interesting, I had thought the library used that supported mTLS (yes, I'm brain dead and forgot the name) was the thing conflicting with custom CA support.
Seems progress has been made as of July this year. Here's a PR of adding support for user supplied CAs (via the Android Certificate Store) to another Flutter app: https://github.com/jmshrv/finamp/pull/1276/files
This was based off the Dart SDK conversations in: https://github.com/dart-lang/sdk/issues/50435
@rovo89 commented on GitHub (Sep 2, 2025):
This sounds promising: https://github.com/immich-app/immich/pull/19830#issuecomment-3243963077
IIUC, they're planning to replace the network stack with Cronet on Android, which is based on Chromium, so might use the system trust store out if the box. Not sure about e.g. video player which already uses the native client, and about mTLS in general (needs a few steps to choose and allow a certificate), let's see.
@polomani commented on GitHub (Sep 10, 2025):
@rovo89 does the new beta timeline work for you with the mTLS enabled?
I am getting 403s (mTLS failure) and basically cannot backup using beta timeline: https://github.com/immich-app/immich/issues/21006#issuecomment-3275734409
@amigthea commented on GitHub (Sep 10, 2025):
beta timeline over mTLS is working for me
@rovo89 commented on GitHub (Sep 10, 2025):
Yes, if works for me. Not too sure about the background upload (might need some toggling off and on), but at least when I open the app, it syncs without requiring any actions from my side.
@willm commented on GitHub (Sep 13, 2025):
Mtls with the beta timeline is not working for me. I'm curious though about the original message saying these features are not needed because of others. I use mtls to expose immich publicly with trusted users. It's a one time setup to install the certificate on my family's devices. A lot of people suggest some kind of VPN solution, but that's another app to install, enable, have an account on and host. What's the official recommended way to securely expose immich over the internet with minimal fuss?
@mmomjian commented on GitHub (Sep 13, 2025):
The officially recommended ways would be a VPN or a reverse proxy with a real SSL certificate.
@jamesonuk commented on GitHub (Sep 13, 2025):
There is no such thing as a real SSL certificate and you are mixing up server and client certificates. I presume you are talking about certificates issued by one on the main CAs (the ones that browsers trust by default)?
Server certificates are for clients to have some confidence the server is who it claims to be (through trust).
Client certificates are for the server to check the client is who they say they are.
It is quite possible to have a reverse proxy with a "real SSL" certificate but use a completely different CA for client certificates.
Full two way TLS (mTLS) is about the client trusting the server and the server trusting the client which is what I believe most people are talking about here.
@mmomjian commented on GitHub (Sep 13, 2025):
Yes; I mean a server SSL certificate that is issued by a default/trusted global CA. We don’t officially recommend client certificates in any capacity.
@nordewal commented on GitHub (Sep 13, 2025):
Running immich behing "a reverse proxy with a real SSL certificate" allows anyone to connect to the immich server. Client certificates ("mTLS") are usually used in addition with "a reverse proxy with a real SSL certificate."
People (or at least me) are using mTLS to protect the immich setup/server from bad actors.
Immich is still rather young and likely has many (un-)known vulnerabilities - even though the team is doing is really great job. Running it behind a reverse proxy that requires a client certificate ("mTLS") ensures that only trusted clients can reach the immich server/API.
This is similar to a VPN, however much more convenient. Why? Because I just need to add the client certificate on the devices that should be able to reach my immich instance (my phone & my wife's phone). This removes the need for any other app/VPN client to be installed and running.
@rovo89 commented on GitHub (Sep 13, 2025):
For exactly these reasons, I use mTLS for my services. It is working for me though with Immich, including the beta timeline, so it would be worth investigating what the problem is for you. You surely have uploaded the certificate in the app and it worked with the old timeline?
@willm commented on GitHub (Sep 13, 2025):
My tiny server that I run as an enthusiast in my spare time doesn't have the massive team that a mainstream website or photo serving service has. I already have a reverse proxy with a real let's encrypt certificate, rate limiting and fail2ban on it. A VPN makes things unusable for my family so client certificates offer a way to guarantee that only they can access the site.
Maybe TLS + http basic auth with a long password would do the trick, is that officially supported?
@bo0tzz commented on GitHub (Sep 13, 2025):
Not quite. Whether you're using a reverse proxy, a cert from a browser-trusted CA, or whether the Immich instance is publically accessible are all entirely unrelated to eachother.
@EssGeeEich commented on GitHub (Sep 13, 2025):
Still, you're missing the point, like many others in this thread (including some that I think are active developers in this project).
Using a custom CA's certificate or a public CA's certificate does not block an attacker from bruteforcing a login or exploit known (or eventually unknown) security holes, since it's just a way of saying that that server is whoever it says it is.
mTLS, on the other hand, does, since it requires that the CLIENT has a certificate that the server approves - and is a way to forcefully verify that a CLIENT is whoever says it is, before the server even interprets its request -- ESPECIALLY if the mTLS check happens in the reverse proxy, like all of the mTLS users are doing right now.
Since this works before the Immich daemon receives the request, any security issue on Immich is mitigated by this check, since an attacker would need a fully verified client key in order to even attempt to attack the Immich server.
@mmomjian commented on GitHub (Sep 13, 2025):
If complete protection with a private key (a la mTLS) is what you are after, we would recommend a VPN. No one is suggesting a reverse proxy with a globally-trusted TLS cert provides verification of the client identity. OIDC/OAuth may be a workable middle ground here if you are comfortable with setting that up.
@amigthea commented on GitHub (Sep 13, 2025):
I agree with people saying that VPN would be an hassle to mantain in comparison. My setup has both mTLS and OIDC identity check, that should be considered as complementary and not an alternative
@polomani commented on GitHub (Sep 13, 2025):
I think we need to debug the issue with the mTLS + beta timeline backup myself and @willm is getting.
I poked a bit, and since the timeline worked for @rovo89 @amigthea I figured it was something with my setup, so I found the culprit. The error
Failed to set SSL options: Null cannot be cast to non-null type kotlin.Stringwas caused due to the cert not having the password.dad81af6e3/mobile/lib/utils/http_ssl_options.dart (L38)Once I've created a certificate with a passkey, the timeline backup started to work on Android.
However, that didn't solve the issue on IOS, so I suspect it's something different there.
I use haproxy, so I see in the logs that the certificate was not passed in
Example of the log with correct client cert:
So by filtering the
/api/assetsendpoint from mTLS check I managed to work around the issue both on Android and IOS for me, but it's of course not ideal.Maybe any devs here know what is different when /api/assets endpoint is being called from the app?
@willm commented on GitHub (Sep 13, 2025):
I'm on android and no my client cert doesn't have a password, that error message is exactly what I'm getting, anyway, I also have users on iOS.
I should also reference this issue where I'm specifically talking about this.
@ianhattendorf commented on GitHub (Sep 26, 2025):
Just noticed this issue, also posted in https://github.com/immich-app/immich/issues/22320#issuecomment-3336466719. Backups are failing to upload for me when mTLS is enabled on the beta timeline from the iOS app. I'm using a letsencrypt cert for the server cert and a cert from a trusted private CA for the client cert.
@Blurazzle commented on GitHub (Sep 26, 2025):
Hopefully not redundant, and it might even be a known issue. But on my iPhone with iOS 18.7, Immich version 1.43.1 is not able to upload new images when I use a self‑signed certificate on an Nginx reverse proxy. The test was done with the “new timeline” option under Advanced Settings enabled.
Nginx has the advised configuration, and the certificates are made with mkcert. Web and Android do not have any problem with the self‑signed certificate. On my iPhone all functions work with the self‑signed certificate as far as I have been able to test, but uploading new images fails. When I replace the self‑signed certificate with a certificate from Let’s Encrypt, the problem is resolved on the iPhone app.
Reading the discussion here, I think it is important for Immich at some point to define clearly what is supported and what isn’t. I would like to have mTLS working (not yet tested), but if it is not possible to support it consistently because of complexity in Immich’s tech stack, it might be better to remove it.
@bunsenhd commented on GitHub (Sep 26, 2025):
@jonkerw85 iOS and self signed is a real pain as they are never trusted by the OS unless you explicitly trust in Certificate Trust Settings... using a wildcard LE cert on your proxy, this is the way :).
On my setup, I use a wildcard LE certificate for the proxy, and a Private CA for MTLS. My problem is with the new backup system (on iPhone). If you have MTLS enabled, it does not work as it does not present the client certificate when connecting to the server (via proxy). --
I see the next build has deprecation for the old timeline, which means no more backups with MTLS??
I do hope the new backup picks up MTLS :)
@polomani commented on GitHub (Sep 26, 2025):
Reading this PR from @rovo89 https://github.com/immich-app/immich/pull/16403 I am getting the impression that a similar change has to be done to make mTLS with the beta timeline work on IOS.
There are parts of the app (presumably background workers) where HttpOverride has to be done natively.
I could probably write something (with the help of LLMs), but there is no way for me to make a build to test it out, unfortunately.
@rovo89 commented on GitHub (Sep 26, 2025):
Also check https://github.com/immich-app/immich/pull/21459#issuecomment-3315212905 and following comments.
@performation9 commented on GitHub (Oct 7, 2025):
Backups stopped working for me with the new timeline and mTLS as well on iOS. Is there any help needed in debugging? I am unfortunately no programmer and cannot attempt to fix the issue.
@denysvitali commented on GitHub (Oct 8, 2025):
On the same note, is there any plan to finally support mTLS with CA verification in every part of the mobile codebase? For example, on Android it shouldn't be necessary to import the client credentials as PFX if they're already imported in the user trust store. Same applies for user trusted CAs.
Please do let me know if I should create a separate issue for this.
@kaaax0815 commented on GitHub (Oct 13, 2025):
I think a recap is needed as this thread is quite large.
mTLS does not properly work on android or ios.
using basic auth or custom headers does work on both platforms.
is that correct?
@performation9 commented on GitHub (Oct 13, 2025):
I can confirm backups are not working with mTLS on iOS. logging in and viewing photos does work.
@felixconsulting commented on GitHub (Oct 14, 2025):
I get the
Trust anchor for certification path not validerror only when trying to play videos, and they refuse to play. Nothing else is impacted by this. I have a self signed certificate, the rootCA is imported to android, and the certificate my reverse proxy uses is under the rootCA android has. I'm using grapheneos with immich 2.0.1. Openssl saysVerify return code: 19 (self-signed certificate in certificate chain)which suggests that it is an android app issue as opposed to a flawed certificate chain. I also have this issue with chora, a subsonic client which connects to a server with a similiar certificate chain, but no other apps I use have this problem. It seems I am the only person with this issue on immich or chora. I would appreciate any attention to this matter. Please not this ONLY applies to the mobile app, web video playback is fully functioning.edit: This issue is likely identical to #5553, which is closed for some reason?
@imjustmatthew commented on GitHub (Oct 15, 2025):
It would be useful to mark these features in the app UI as experimental/unusable or link to this tracking issue on the advanced network settings page. These features are basically unusable and the app UI does not make that apparent.
@mmomjian commented on GitHub (Oct 15, 2025):
@felixconsulting this is a know limitation with using your own RootCA, we don’t fully support this feature.
We will look into marking these features in the app as experimental. This was done in the past but seems to have been regressed.
@denysvitali commented on GitHub (Oct 16, 2025):
FYI: if we switch to OkHttp (see #22768 in draft), User CAs work perfectly fine. Same goes for mTLS - and the real issue seems to be that OkHttp doesn't support well websockets (or simply put, we need to rewrite a bit the interface so that we can use the OkHttp impl and the dart-io one).
@felixconsulting commented on GitHub (Oct 18, 2025):
@mmomjian It seems very hit or miss with android apps and my custom rootca, is there a way to just flick a switch and read those imported CAs like the default ones? it seems like thats more of the issue cause people with lets encrypt don't have this problem
@mmomjian commented on GitHub (Oct 18, 2025):
No, if there was a switch to flick we would not have this issue to begin with :P
@jeverling commented on GitHub (Oct 23, 2025):
I'm affected by problems with basic auth, the Android app isn't able to back up photos anymore. A single photo out of 25 new ones went throught actually, for the rest I get a 401 error, and the app retries them (indefinitely?).
This used to work before (at some point in the past it was broken too, but that got fixed).
Does anybody know which version broke this functionality again so I can downgrade?
And since we don't have individual tickets for the Experimental network issues, is someone maybe already working on this?
@Syoc commented on GitHub (Oct 25, 2025):
Hi. Can confirm that the iOS app does not use the client certificate for the /api/assets endpoint.
Regarding alternatives to mutual TLS authentication. A large part of keeping internet exposed infrastructure secure is managing attack surface. Exposing the app specific authentication logic, be that username and password or OIDC, is a much larger attack surface and not acceptable by a lot of people. A VPN connection is more comparable, but it is a lot to impose on other instance users. There are also power consumption issues on mobile devices. Users with existing VPN connection also have to handle complex routing setups.
@nolith commented on GitHub (Nov 17, 2025):
I should have read this before implementing mTLS and reworking my entire proxy setup.
So I moved to mTLS to relief my wife from running the VPN client, it works perfectly on browser and for general usage of the app, except it does not allow to upload images.
I managed to workaround that by tweaking my nginx config and remove the mTLS validation only for
/api/assetsFrom the log we can see the user agent is different between regular app usage and the background upload. Also, before redacting the IP addresses, I could dee that
Immich_iOS_2.2.3was using IPv4 whileimmich_mobile/235was using IPv6.@ZoTay commented on GitHub (Nov 18, 2025):
@nolith could you share your snippet of your nginx proxy to remove validation for that specific uri?
@nolith commented on GitHub (Nov 18, 2025):
Sure, here is my config @ZoTay.
The key element are:
ssl_verify_client optional;,if ($ssl_client_verify != SUCCESS) { return 418; }to location/,/api/assets.@denysvitali commented on GitHub (Nov 19, 2025):
FYI, the reason why you see different user agents and fail the mTLS part is because the HttpClient being used there is not the same one (global one), thus it's missing the configuration.
In #22768 I fixed that and use the keystore for mTLS
@nolith commented on GitHub (Nov 19, 2025):
Thanks for your work.
When I first looked at that PR I got the impression it was only fixing Android. Does it also handle iOS?
@denysvitali commented on GitHub (Nov 19, 2025):
It's only for Android (the mTLS part) but the changes related to using the global HTTP client are valid for all platforms
@waleedhad commented on GitHub (Nov 26, 2025):
I'm also experiencing failure to back up when using the beta/new timeline on iOS. Android devices and web works fine with the beta/new timeline enabled and same exact setup. My server and app are configured to use mTLS via Nginx as a reverse proxy. Switching back to the old timeline on iOS fixes the issue. I am strong proponent of mtls and I think its needed for security even though I have OIDC/OAuth configured and enabled as the only login option. I hope mtls is supported fully in the app.
@webvictim commented on GitHub (Nov 26, 2025):
I have the same issue; mTLS does not work correctly using the new timeline on iOS. Works fine when I switch back to the old timeline. Also reported here: https://github.com/immich-app/immich/issues/22320#issuecomment-3437913692
@crysxd commented on GitHub (Nov 27, 2025):
I discovered an issue with Basic Auth URLs. I run my Immich behind a reverse proxy which has a Basic Auth configured. The immich URL in the app is https://user:pw@myimmich.com
On iOS everything works perfectly. On Android, you can sign in and see pictures without issues but the uploads fail with a 401 error, seems like the URL is loosing the Basic Auth information for the upload
@t-gh-ctrl commented on GitHub (Dec 30, 2025):
I confirm @crysxd 's findings above: with everything up to date as of writing (2.4.1), a reverse proxy (nginx) with basic auth configured, the immich app on iOS works fine with a user:pass@site address while uploads fail with 401 on android.
Despite configuring the server with user:pass@site, the server shows up as "site" in the app's settings (but maybe that's just cosmetic). I tried to set a user:pass@site as a custom server depending on the wifi network the phone is connected to, but it doesn't work either.
Temporary workarounds: set up a vpn (eg. wireguard) and configure the reverse proxy to skip basic auth for vpn clients; or skip basic auth depending on the client's properties like country networks and user agent (
Immich_Android_.*/Dalvik/.*), and/or a custom proxy header set in the android app.@pippo73 commented on GitHub (Jan 3, 2026):
Hello all,
Since some weeks I'm moving my system from a FQDN to a tailnet
For this reason I have a CA selfsigned.
I've discovered that the ssl is not working with such certificates.
In the settings of the app, I've noticed that I should be able to add a CA to the app, but this features are not working even if present. How can I enable it?
@jfly commented on GitHub (Jan 3, 2026):
@pippo73, I suggest reading through https://github.com/immich-app/immich/pull/19830 to learn more about self-signed certificates.
@ramanenka commented on GitHub (Jan 4, 2026):
On iOS widgets don't work when the reverse proxy in front of Immich is requiring client certificate despite specifying the client cert in "SSL client certificate" in advanced settings.
@bunsenhd commented on GitHub (Jan 30, 2026):
seems 2.5 onward has broken MTLS even further. You can 1) log in with MTLS enabled, 2) see local assets, but 3) Thumbnails fail, 4) remote images fail, and 5) backups fail with new time enabled. I guess with the new sync, MTLS is not in the mix.
Anyone else see the issues on IOS?
@joutain commented on GitHub (Jan 30, 2026):
same observation as @bunsenhd , ios could see the assets from the server behind proxy and mtls, but can't show thumbnails, and click the photo not showing anything, in the log showing bunch of below. but when in local network with http://192.168.1.x:2283, all the phoeo, video, thumbnails showing properly again.
android seems to be working fine with proxy and mtls. ios specific problem
"Error loading image:
PlatformException(, Failed to decode image from request, null, null)"
@krikk commented on GitHub (Jan 30, 2026):
i still have the "old" timeline and all of the above seem to work for me (newest iOS)
@Steven-- commented on GitHub (Jan 30, 2026):
I see exactly the same issues reported by @bunsenhd but on Android using Immich 2.5.1.
I am logged in, but thumbnails and remote images fail. I also cannot upload any local asset. I'm using the new timeline (I haven't tried the old one with this newer version).
@denysvitali commented on GitHub (Feb 1, 2026):
Yes - people - the problem is that the new timeline introduced a new HTTP client that doesn't have the same settings as the client that gets configured for the old timeline.
Server logs show this behavior too, since the User-Agent switches depending on the code path.
IMHO https://github.com/immich-app/immich/pull/22768 should have been merged and this problem would have been fixed, but the authors of this project don't seem to care about this particular issue.
I'm sorry - I kind of stopped using Immich after this 🙃
TL;DR: the real problem is that the codebase uses 3 / 4 different HTTP clients, and only one or two of them are configured w/ mTLS support / User-Agent
@mertalev commented on GitHub (Feb 2, 2026):
Sorry for the inconvenience. We're working on fixing mTLS on iOS. This is a part of a transition toward using only one client across the app, meaning one place to configure these settings and a smoother experience overall.
@Daniel-dev22 commented on GitHub (Feb 2, 2026):
Is mtls on Android also being looked at?
@mertalev commented on GitHub (Feb 2, 2026):
mTLS should already work on Android. What might not be working is the "allow self-signed certificates" option, meaning you might need to add the CA cert to the OS store.
@jfly commented on GitHub (Feb 2, 2026):
There is an (pretty dangerous) "allow any certificate feature. That works on Android, but I wouldn't use it myself.
This does not work on Android. Or, more accurately, there are 2 types of certificates in the Android OS store: "system certificates" and "user-installed certificates". System certificates work, but require a rooted device to add new ones. User-installed certificates do not work. #19830 made them work, but unfortunately that PR was rejected.
@mertalev commented on GitHub (Feb 2, 2026):
Hmm, changing the network security configuration to trust user-installed certs should be enough to fix that, right?
@jfly commented on GitHub (Feb 2, 2026):
@mertalev, that works for the "native" android networking stack, but flutter's (actually, dart's) network stack does its own thing which is completely unaware of user-installed certificates on android. Please see my PR description (https://github.com/immich-app/immich/pull/19830) for more details and a link to upstream issue(s).
@mertalev commented on GitHub (Feb 2, 2026):
Right, so it will work as long as the rest of the app also uses native clients.
@jfly commented on GitHub (Feb 2, 2026):
Yes. I was trying to make it clear what the current state of things is. Looking forward to the status quo changing, though!
@alextran1502 commented on GitHub (Feb 2, 2026):
Hello, just want to jump in here. Mert's been heads down on this and putting in real effort to get it sorted. I appreciate your patience while he works through it.
I get that it's frustrating when something breaks, but the networking feature is marked experimental for a reason: it's still evolving and not guaranteed to be stable, as we are still working through the tech debt in the mobile codebase. That's the trade-off with opting into experimental features early.
Self-hosted networking setups vary widely, and while we test against common configurations, we're still working through edge cases. That's just where we're at with this one.
As for not caring, we definitely do. We're just a small team trying to juggle a lot, and sometimes things take longer than any of us would like. I wish we had more people with deep networking knowledge to throw at this. We're doing what we can.
Thanks for understanding, and please help keep the discussion pleasant for all sides involved.