diff --git a/mobile/assets/i18n/en-US.json b/mobile/assets/i18n/en-US.json
index 3b8db15987..139c5895b1 100644
--- a/mobile/assets/i18n/en-US.json
+++ b/mobile/assets/i18n/en-US.json
@@ -531,5 +531,19 @@
   "version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89",
   "viewer_remove_from_stack": "Remove from Stack",
   "viewer_stack_use_as_main_asset": "Use as Main Asset",
-  "viewer_unstack": "Un-Stack"
-}
\ No newline at end of file
+  "viewer_unstack": "Un-Stack",
+  "header_settings_header_name_input": "Header name",
+  "header_settings_header_value_input": "Header value",
+  "header_settings_page_title": "Proxy Headers",
+  "header_settings_add_header_tip": "Add Header",
+  "header_settings_field_validator_msg": "Value cannot be empty",
+  "client_cert_title": "SSL Client Certificate",
+  "client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate Import/Remove is available only before login",
+  "client_cert_import": "Import",
+  "client_cert_remove": "Remove",
+  "client_cert_remove_msg": "Client certificate is removed",
+  "client_cert_import_success_msg": "Client certificate is imported",
+  "client_cert_invalid_msg": "Invalid certificate file or wrong password",
+  "client_cert_dialog_msg_confirm": "OK",
+  "client_cert_enter_password": "Enter Password"
+}
diff --git a/mobile/ios/Podfile.lock b/mobile/ios/Podfile.lock
index 7cda1122b1..39938b020a 100644
--- a/mobile/ios/Podfile.lock
+++ b/mobile/ios/Podfile.lock
@@ -4,6 +4,40 @@ PODS:
     - ReachabilitySwift
   - device_info_plus (0.0.1):
     - Flutter
+  - DKImagePickerController/Core (4.3.9):
+    - DKImagePickerController/ImageDataManager
+    - DKImagePickerController/Resource
+  - DKImagePickerController/ImageDataManager (4.3.9)
+  - DKImagePickerController/PhotoGallery (4.3.9):
+    - DKImagePickerController/Core
+    - DKPhotoGallery
+  - DKImagePickerController/Resource (4.3.9)
+  - DKPhotoGallery (0.0.19):
+    - DKPhotoGallery/Core (= 0.0.19)
+    - DKPhotoGallery/Model (= 0.0.19)
+    - DKPhotoGallery/Preview (= 0.0.19)
+    - DKPhotoGallery/Resource (= 0.0.19)
+    - SDWebImage
+    - SwiftyGif
+  - DKPhotoGallery/Core (0.0.19):
+    - DKPhotoGallery/Model
+    - DKPhotoGallery/Preview
+    - SDWebImage
+    - SwiftyGif
+  - DKPhotoGallery/Model (0.0.19):
+    - SDWebImage
+    - SwiftyGif
+  - DKPhotoGallery/Preview (0.0.19):
+    - DKPhotoGallery/Model
+    - DKPhotoGallery/Resource
+    - SDWebImage
+    - SwiftyGif
+  - DKPhotoGallery/Resource (0.0.19):
+    - SDWebImage
+    - SwiftyGif
+  - file_picker (0.0.1):
+    - DKImagePickerController/PhotoGallery
+    - Flutter
   - Flutter (1.0.0)
   - flutter_local_notifications (0.0.1):
     - Flutter
@@ -46,6 +80,9 @@ PODS:
     - FlutterMacOS
   - ReachabilitySwift (5.0.0)
   - SAMKeychain (1.5.3)
+  - SDWebImage (5.19.4):
+    - SDWebImage/Core (= 5.19.4)
+  - SDWebImage/Core (5.19.4)
   - share_plus (0.0.1):
     - Flutter
   - shared_preferences_foundation (0.0.1):
@@ -54,6 +91,7 @@ PODS:
   - sqflite (0.0.3):
     - Flutter
     - FMDB (>= 2.7.5)
+  - SwiftyGif (5.4.5)
   - Toast (4.0.0)
   - url_launcher_ios (0.0.1):
     - Flutter
@@ -66,6 +104,7 @@ PODS:
 DEPENDENCIES:
   - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
   - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
+  - file_picker (from `.symlinks/plugins/file_picker/ios`)
   - Flutter (from `Flutter`)
   - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
   - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
@@ -91,10 +130,14 @@ DEPENDENCIES:
 
 SPEC REPOS:
   trunk:
+    - DKImagePickerController
+    - DKPhotoGallery
     - FMDB
     - MapLibre
     - ReachabilitySwift
     - SAMKeychain
+    - SDWebImage
+    - SwiftyGif
     - Toast
 
 EXTERNAL SOURCES:
@@ -102,6 +145,8 @@ EXTERNAL SOURCES:
     :path: ".symlinks/plugins/connectivity_plus/ios"
   device_info_plus:
     :path: ".symlinks/plugins/device_info_plus/ios"
+  file_picker:
+    :path: ".symlinks/plugins/file_picker/ios"
   Flutter:
     :path: Flutter
   flutter_local_notifications:
@@ -150,6 +195,9 @@ EXTERNAL SOURCES:
 SPEC CHECKSUMS:
   connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
   device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
+  DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
+  DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
+  file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
   Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
   flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
   flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
@@ -170,9 +218,11 @@ SPEC CHECKSUMS:
   photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
   ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
   SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
+  SDWebImage: 066c47b573f408f18caa467d71deace7c0f8280d
   share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
   shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
   sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
+  SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
   Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
   url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
   video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579
diff --git a/mobile/lib/entities/store.entity.dart b/mobile/lib/entities/store.entity.dart
index 55d252b307..baa7ff51a3 100644
--- a/mobile/lib/entities/store.entity.dart
+++ b/mobile/lib/entities/store.entity.dart
@@ -1,3 +1,6 @@
+import 'dart:convert';
+import 'dart:typed_data';
+
 import 'package:collection/collection.dart';
 import 'package:immich_mobile/entities/user.entity.dart';
 import 'package:isar/isar.dart';
@@ -140,6 +143,36 @@ class StoreValue {
   }
 }
 
+class SSLClientCertStoreVal {
+  final Uint8List data;
+  final String? password;
+
+  SSLClientCertStoreVal(this.data, this.password);
+
+  void save() {
+    final b64Str = base64Encode(data);
+    Store.put(StoreKey.sslClientCertData, b64Str);
+    if (password != null) {
+      Store.put(StoreKey.sslClientPasswd, password!);
+    }
+  }
+
+  static SSLClientCertStoreVal? load() {
+    final b64Str = Store.tryGet<String>(StoreKey.sslClientCertData);
+    if (b64Str == null) {
+      return null;
+    }
+    final Uint8List certData = base64Decode(b64Str);
+    final passwd = Store.tryGet<String>(StoreKey.sslClientPasswd);
+    return SSLClientCertStoreVal(certData, passwd);
+  }
+
+  static void delete() {
+    Store.delete(StoreKey.sslClientCertData);
+    Store.delete(StoreKey.sslClientPasswd);
+  }
+}
+
 class StoreKeyNotFoundException implements Exception {
   final StoreKey key;
   StoreKeyNotFoundException(this.key);
@@ -164,6 +197,8 @@ enum StoreKey<T> {
   serverEndpoint<String>(12, type: String),
   autoBackup<bool>(13, type: bool),
   backgroundBackup<bool>(14, type: bool),
+  sslClientCertData<String>(15, type: String),
+  sslClientPasswd<String>(16, type: String),
   // user settings from [AppSettingsEnum] below:
   loadPreview<bool>(100, type: bool),
   loadOriginal<bool>(101, type: bool),
diff --git a/mobile/lib/utils/http_ssl_cert_override.dart b/mobile/lib/utils/http_ssl_cert_override.dart
index 25d22b6812..7794831adb 100644
--- a/mobile/lib/utils/http_ssl_cert_override.dart
+++ b/mobile/lib/utils/http_ssl_cert_override.dart
@@ -4,12 +4,49 @@ import 'package:immich_mobile/entities/store.entity.dart';
 import 'package:logging/logging.dart';
 
 class HttpSSLCertOverride extends HttpOverrides {
+  static final Logger _log = Logger("HttpSSLCertOverride");
+  final SSLClientCertStoreVal? _clientCert;
+  late final SecurityContext? _ctxWithCert;
+
+  HttpSSLCertOverride() : _clientCert = SSLClientCertStoreVal.load() {
+    if (_clientCert != null) {
+      _ctxWithCert = SecurityContext(withTrustedRoots: true);
+      if (_ctxWithCert != null) {
+        setClientCert(_ctxWithCert, _clientCert);
+      } else {
+        _log.severe("Failed to create security context with client cert!");
+      }
+    } else {
+      _ctxWithCert = null;
+    }
+  }
+
+  static bool setClientCert(SecurityContext ctx, SSLClientCertStoreVal cert) {
+    try {
+      _log.info("Setting client certificate");
+      ctx.usePrivateKeyBytes(cert.data, password: cert.password);
+      if (!Platform.isIOS) {
+        ctx.useCertificateChainBytes(cert.data, password: cert.password);
+      }
+    } catch (e) {
+      _log.severe("Failed to set SSL client cert: $e");
+      return false;
+    }
+    return true;
+  }
+
   @override
   HttpClient createHttpClient(SecurityContext? context) {
+    if (context != null) {
+      if (_clientCert != null) {
+        setClientCert(context, _clientCert);
+      }
+    } else {
+      context = _ctxWithCert;
+    }
+
     return super.createHttpClient(context)
       ..badCertificateCallback = (X509Certificate cert, String host, int port) {
-        var log = Logger("HttpSSLCertOverride");
-
         AppSettingsEnum setting = AppSettingsEnum.allowSelfSignedSSLCert;
 
         // Check if user has allowed self signed SSL certificates.
@@ -28,7 +65,7 @@ class HttpSSLCertOverride extends HttpOverrides {
         }
 
         if (!selfSignedCertsAllowed) {
-          log.severe("Invalid SSL certificate for $host:$port");
+          _log.severe("Invalid SSL certificate for $host:$port");
         }
 
         return selfSignedCertsAllowed;
diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart
index 60ad4ea3d3..ec1ab79cf7 100644
--- a/mobile/lib/widgets/settings/advanced_settings.dart
+++ b/mobile/lib/widgets/settings/advanced_settings.dart
@@ -13,6 +13,7 @@ import 'package:immich_mobile/services/app_settings.service.dart';
 import 'package:immich_mobile/providers/user.provider.dart';
 import 'package:immich_mobile/services/immich_logger.service.dart';
 import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
+import 'package:immich_mobile/widgets/settings/ssl_client_cert_settings.dart';
 import 'package:logging/logging.dart';
 
 class AdvancedSettings extends HookConsumerWidget {
@@ -64,6 +65,7 @@ class AdvancedSettings extends HookConsumerWidget {
         onChanged: (_) => HttpOverrides.global = HttpSSLCertOverride(),
       ),
       const CustomeProxyHeaderSettings(),
+      SslClientCertSettings(isLoggedIn: ref.read(currentUserProvider) != null),
     ];
 
     return SettingsSubPageScaffold(settings: advancedSettings);
diff --git a/mobile/lib/widgets/settings/ssl_client_cert_settings.dart b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart
new file mode 100644
index 0000000000..0daddd6d88
--- /dev/null
+++ b/mobile/lib/widgets/settings/ssl_client_cert_settings.dart
@@ -0,0 +1,158 @@
+import 'dart:io';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:file_picker/file_picker.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:immich_mobile/entities/store.entity.dart';
+import 'package:immich_mobile/extensions/build_context_extensions.dart';
+import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
+
+class SslClientCertSettings extends StatefulWidget {
+  const SslClientCertSettings({super.key, required this.isLoggedIn});
+
+  final bool isLoggedIn;
+
+  @override
+  State<StatefulWidget> createState() => _SslClientCertSettingsState();
+}
+
+class _SslClientCertSettingsState extends State<SslClientCertSettings> {
+  _SslClientCertSettingsState()
+      : isCertExist = SSLClientCertStoreVal.load() != null;
+
+  bool isCertExist;
+
+  @override
+  Widget build(BuildContext context) {
+    return ListTile(
+      contentPadding: const EdgeInsets.symmetric(horizontal: 20),
+      horizontalTitleGap: 20,
+      isThreeLine: true,
+      title: Text(
+        "client_cert_title".tr(),
+        style: context.textTheme.bodyLarge?.copyWith(
+          fontWeight: FontWeight.w500,
+        ),
+      ),
+      subtitle: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          Text(
+            "client_cert_subtitle".tr(),
+            style: context.textTheme.bodyMedium,
+          ),
+          const SizedBox(
+            height: 6,
+          ),
+          Row(
+            mainAxisSize: MainAxisSize.max,
+            mainAxisAlignment: MainAxisAlignment.center,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              ElevatedButton(
+                onPressed: widget.isLoggedIn ? null : () => importCert(context),
+                child: Text("client_cert_import".tr()),
+              ),
+              const SizedBox(
+                width: 15,
+              ),
+              ElevatedButton(
+                onPressed: widget.isLoggedIn || !isCertExist
+                    ? null
+                    : () => removeCert(context),
+                child: Text("client_cert_remove".tr()),
+              ),
+            ],
+          ),
+        ],
+      ),
+    );
+  }
+
+  void showMessage(BuildContext context, String message) {
+    showDialog(
+      context: context,
+      builder: (ctx) => AlertDialog(
+        content: Text(message),
+        actions: [
+          TextButton(
+            onPressed: () => ctx.pop(),
+            child: Text("client_cert_dialog_msg_confirm".tr()),
+          ),
+        ],
+      ),
+    );
+  }
+
+  void storeCert(BuildContext context, Uint8List data, String? password) {
+    if (password != null && password.isEmpty) {
+      password = null;
+    }
+    final cert = SSLClientCertStoreVal(data, password);
+    // Test whether the certificate is valid
+    final isCertValid = HttpSSLCertOverride.setClientCert(
+      SecurityContext(withTrustedRoots: true),
+      cert,
+    );
+    if (!isCertValid) {
+      showMessage(context, "client_cert_invalid_msg".tr());
+      return;
+    }
+    cert.save();
+    HttpOverrides.global = HttpSSLCertOverride();
+    setState(
+      () => isCertExist = true,
+    );
+    showMessage(context, "client_cert_import_success_msg".tr());
+  }
+
+  void setPassword(BuildContext context, Uint8List data) {
+    final password = TextEditingController();
+    showDialog(
+      context: context,
+      barrierDismissible: false,
+      builder: (ctx) => AlertDialog(
+        content: TextField(
+          controller: password,
+          obscureText: true,
+          obscuringCharacter: "*",
+          decoration: InputDecoration(
+            hintText: "client_cert_enter_password".tr(),
+          ),
+        ),
+        actions: [
+          TextButton(
+            onPressed: () =>
+                {ctx.pop(), storeCert(context, data, password.text)},
+            child: Text("client_cert_dialog_msg_confirm".tr()),
+          ),
+        ],
+      ),
+    );
+  }
+
+  Future<void> importCert(BuildContext ctx) async {
+    FilePickerResult? res = await FilePicker.platform.pickFiles(
+      type: FileType.custom,
+      allowedExtensions: [
+        'p12',
+        'pfx',
+      ],
+    );
+    if (res != null) {
+      File file = File(res.files.single.path!);
+      final bytes = await file.readAsBytes();
+      setPassword(ctx, bytes);
+    }
+  }
+
+  void removeCert(BuildContext context) {
+    SSLClientCertStoreVal.delete();
+    HttpOverrides.global = HttpSSLCertOverride();
+    setState(
+      () => isCertExist = false,
+    );
+    showMessage(context, "client_cert_remove_msg".tr());
+  }
+}
diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock
index fe803b5762..de2c4ce687 100644
--- a/mobile/pubspec.lock
+++ b/mobile/pubspec.lock
@@ -277,10 +277,10 @@ packages:
     dependency: transitive
     description:
       name: cross_file
-      sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9"
+      sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
       url: "https://pub.dev"
     source: hosted
-    version: "0.3.3+4"
+    version: "0.3.3+8"
   crypto:
     dependency: transitive
     description:
@@ -417,6 +417,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "7.0.0"
+  file_picker:
+    dependency: "direct main"
+    description:
+      name: file_picker
+      sha256: d1d0ac3966b36dc3e66eeefb40280c17feb87fa2099c6e22e6a1fc959327bd03
+      url: "https://pub.dev"
+    source: hosted
+    version: "8.0.0+1"
   file_selector_linux:
     dependency: transitive
     description:
@@ -548,10 +556,10 @@ packages:
     dependency: transitive
     description:
       name: flutter_plugin_android_lifecycle
-      sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360"
+      sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e
       url: "https://pub.dev"
     source: hosted
-    version: "2.0.15"
+    version: "2.0.20"
   flutter_riverpod:
     dependency: transitive
     description:
@@ -1186,10 +1194,10 @@ packages:
     dependency: transitive
     description:
       name: plugin_platform_interface
-      sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd"
+      sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.5"
+    version: "2.1.8"
   pointycastle:
     dependency: transitive
     description:
@@ -1759,18 +1767,18 @@ packages:
     dependency: transitive
     description:
       name: win32
-      sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c"
+      sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
       url: "https://pub.dev"
     source: hosted
-    version: "4.1.4"
+    version: "5.2.0"
   win32_registry:
     dependency: transitive
     description:
       name: win32_registry
-      sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae"
+      sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a"
       url: "https://pub.dev"
     source: hosted
-    version: "1.1.0"
+    version: "1.1.2"
   xdg_directories:
     dependency: transitive
     description:
@@ -1804,5 +1812,5 @@ packages:
     source: hosted
     version: "3.1.2"
 sdks:
-  dart: ">=3.3.0 <4.0.0"
+  dart: ">=3.4.0 <4.0.0"
   flutter: ">=3.22.2"
diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml
index 2249b83e16..c94407c4d2 100644
--- a/mobile/pubspec.yaml
+++ b/mobile/pubspec.yaml
@@ -68,6 +68,7 @@ dependencies:
   # easy to remove packages:
   image_picker: ^1.0.7 # only used to select user profile image from system gallery -> we can simply select an image from within immich?
   logging: ^1.2.0
+  file_picker: ^8.0.0+1
 
 # This is uncommented in F-Droid build script
 # Taken from https://github.com/Myzel394/locus/blob/445013d22ec1d759027d4303bd65b30c5c8588c8/pubspec.yaml#L105