diff --git a/mobile/lib/utils/url_helper.dart b/mobile/lib/utils/url_helper.dart
index 6b355e362f..187026b53c 100644
--- a/mobile/lib/utils/url_helper.dart
+++ b/mobile/lib/utils/url_helper.dart
@@ -1,5 +1,6 @@
 import 'package:immich_mobile/domain/models/store.model.dart';
 import 'package:immich_mobile/entities/store.entity.dart';
+import 'package:punycode/punycode.dart';
 
 String sanitizeUrl(String url) {
   // Add schema if none is set
@@ -11,13 +12,80 @@ String sanitizeUrl(String url) {
 }
 
 String? getServerUrl() {
-  final serverUrl = Store.tryGet(StoreKey.serverEndpoint);
+  final serverUrl = punycodeDecodeUrl(Store.tryGet(StoreKey.serverEndpoint));
   final serverUri = serverUrl != null ? Uri.tryParse(serverUrl) : null;
   if (serverUri == null) {
     return null;
   }
 
-  return serverUri.hasPort
-      ? "${serverUri.scheme}://${serverUri.host}:${serverUri.port}"
-      : "${serverUri.scheme}://${serverUri.host}";
+  return Uri.decodeFull(
+    serverUri.hasPort
+        ? "${serverUri.scheme}://${serverUri.host}:${serverUri.port}"
+        : "${serverUri.scheme}://${serverUri.host}",
+  );
+}
+
+/// Converts a Unicode URL to its ASCII-compatible encoding (Punycode).
+///
+/// This is especially useful for internationalized domain names (IDNs),
+/// where parts of the URL (typically the host) contain non-ASCII characters.
+///
+/// Example:
+/// ```dart
+/// final encodedUrl = punycodeEncodeUrl('https://bücher.de');
+/// print(encodedUrl); // Outputs: https://xn--bcher-kva.de
+/// ```
+///
+/// Notes:
+/// - If the input URL is invalid, an empty string is returned.
+/// - Only the host part of the URL is converted to Punycode; the scheme,
+///   path, and port remain unchanged.
+///
+String punycodeEncodeUrl(String serverUrl) {
+  final serverUri = Uri.tryParse(serverUrl);
+  if (serverUri == null || serverUri.host.isEmpty) return '';
+
+  final encodedHost = Uri.decodeComponent(serverUri.host).split('.').map(
+    (segment) {
+      // If segment is already ASCII, then return as it is.
+      if (segment.runes.every((c) => c < 0x80)) return segment;
+      return 'xn--${punycodeEncode(segment)}';
+    },
+  ).join('.');
+
+  return serverUri.replace(host: encodedHost).toString();
+}
+
+/// Decodes an ASCII-compatible (Punycode) URL back to its original Unicode representation.
+///
+/// This method is useful for converting internationalized domain names (IDNs)
+/// that were previously encoded with Punycode back to their human-readable Unicode form.
+///
+/// Example:
+/// ```dart
+/// final decodedUrl = punycodeDecodeUrl('https://xn--bcher-kva.de');
+/// print(decodedUrl); // Outputs: https://bücher.de
+/// ```
+///
+/// Notes:
+/// - If the input URL is invalid the method returns `null`.
+/// - Only the host part of the URL is decoded. The scheme and port (if any) are preserved.
+/// - The method assumes that the input URL only contains: scheme, host, port (optional).
+/// - Query parameters, fragments, and user info are not handled (by design, as per constraints).
+///
+String? punycodeDecodeUrl(String? serverUrl) {
+  final serverUri = serverUrl != null ? Uri.tryParse(serverUrl) : null;
+  if (serverUri == null || serverUri.host.isEmpty) return null;
+
+  final decodedHost = serverUri.host.split('.').map(
+    (segment) {
+      if (segment.toLowerCase().startsWith('xn--')) {
+        return punycodeDecode(segment.substring(4));
+      }
+      // If segment is not punycode encoded, then return as it is.
+      return segment;
+    },
+  ).join('.');
+
+  return Uri.decodeFull(serverUri.replace(host: decodedHost).toString());
 }
diff --git a/mobile/lib/widgets/forms/login/login_form.dart b/mobile/lib/widgets/forms/login/login_form.dart
index a6da172f0e..ab532987a7 100644
--- a/mobile/lib/widgets/forms/login/login_form.dart
+++ b/mobile/lib/widgets/forms/login/login_form.dart
@@ -1,4 +1,5 @@
 import 'dart:io';
+
 import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
@@ -7,18 +8,18 @@ import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
 import 'package:fluttertoast/fluttertoast.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:immich_mobile/extensions/build_context_extensions.dart';
-import 'package:immich_mobile/providers/oauth.provider.dart';
-import 'package:immich_mobile/providers/gallery_permission.provider.dart';
-import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/providers/auth.provider.dart';
 import 'package:immich_mobile/providers/backup/backup.provider.dart';
+import 'package:immich_mobile/providers/gallery_permission.provider.dart';
+import 'package:immich_mobile/providers/oauth.provider.dart';
 import 'package:immich_mobile/providers/server_info.provider.dart';
+import 'package:immich_mobile/routing/router.dart';
 import 'package:immich_mobile/utils/provider_utils.dart';
+import 'package:immich_mobile/utils/url_helper.dart';
 import 'package:immich_mobile/utils/version_compatibility.dart';
 import 'package:immich_mobile/widgets/common/immich_logo.dart';
 import 'package:immich_mobile/widgets/common/immich_title_text.dart';
 import 'package:immich_mobile/widgets/common/immich_toast.dart';
-import 'package:immich_mobile/utils/url_helper.dart';
 import 'package:immich_mobile/widgets/forms/login/email_input.dart';
 import 'package:immich_mobile/widgets/forms/login/loading_icon.dart';
 import 'package:immich_mobile/widgets/forms/login/login_button.dart';
@@ -82,7 +83,8 @@ class LoginForm extends HookConsumerWidget {
     /// Fetch the server login credential and enables oAuth login if necessary
     /// Returns true if successful, false otherwise
     Future<void> getServerAuthSettings() async {
-      final serverUrl = sanitizeUrl(serverEndpointController.text);
+      final sanitizeServerUrl = sanitizeUrl(serverEndpointController.text);
+      final serverUrl = punycodeEncodeUrl(sanitizeServerUrl);
 
       // Guard empty URL
       if (serverUrl.isEmpty) {
diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml
index e939c65836..73f60d9337 100644
--- a/mobile/pubspec.yaml
+++ b/mobile/pubspec.yaml
@@ -51,6 +51,7 @@ dependencies:
   permission_handler: ^11.4.0
   photo_manager: ^3.6.4
   photo_manager_image_provider: ^2.2.0
+  punycode: ^1.0.0
   riverpod_annotation: ^2.6.1
   scrollable_positioned_list: ^0.3.8
   share_handler: ^0.0.22
diff --git a/mobile/test/modules/utils/url_helper_test.dart b/mobile/test/modules/utils/url_helper_test.dart
new file mode 100644
index 0000000000..840ac91f1f
--- /dev/null
+++ b/mobile/test/modules/utils/url_helper_test.dart
@@ -0,0 +1,138 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:immich_mobile/utils/url_helper.dart';
+
+void main() {
+  group('punycodeEncodeUrl', () {
+    test('should return empty string for invalid URL', () {
+      expect(punycodeEncodeUrl('not a url'), equals(''));
+    });
+
+    test('should handle empty input', () {
+      expect(punycodeEncodeUrl(''), equals(''));
+    });
+
+    test('should return ASCII-only URL unchanged', () {
+      const url = 'https://example.com';
+      expect(punycodeEncodeUrl(url), equals(url));
+    });
+
+    test('should encode single-segment Unicode host', () {
+      const url = 'https://bücher';
+      const expected = 'https://xn--bcher-kva';
+      expect(punycodeEncodeUrl(url), equals(expected));
+    });
+
+    test('should encode multi-segment Unicode host', () {
+      const url = 'https://bücher.de';
+      const expected = 'https://xn--bcher-kva.de';
+      expect(punycodeEncodeUrl(url), equals(expected));
+    });
+
+    test(
+        'should encode multi-segment Unicode host with multiple non-ASCII segments',
+        () {
+      const url = 'https://bücher.münchen';
+      const expected = 'https://xn--bcher-kva.xn--mnchen-3ya';
+      expect(punycodeEncodeUrl(url), equals(expected));
+    });
+
+    test('should handle URL with port', () {
+      const url = 'https://bücher.de:8080';
+      const expected = 'https://xn--bcher-kva.de:8080';
+      expect(punycodeEncodeUrl(url), equals(expected));
+    });
+
+    test('should handle URL with path', () {
+      const url = 'https://bücher.de/path/to/resource';
+      const expected = 'https://xn--bcher-kva.de/path/to/resource';
+      expect(punycodeEncodeUrl(url), equals(expected));
+    });
+
+    test('should handle URL with port and path', () {
+      const url = 'https://bücher.de:3000/path';
+      const expected = 'https://xn--bcher-kva.de:3000/path';
+      expect(punycodeEncodeUrl(url), equals(expected));
+    });
+
+    test('should not encode ASCII segment in multi-segment host', () {
+      const url = 'https://shop.bücher.de';
+      const expected = 'https://shop.xn--bcher-kva.de';
+      expect(punycodeEncodeUrl(url), equals(expected));
+    });
+
+    test('should handle host with hyphen in Unicode segment', () {
+      const url = 'https://bü-cher.de';
+      const expected = 'https://xn--b-cher-3ya.de';
+      expect(punycodeEncodeUrl(url), equals(expected));
+    });
+
+    test('should handle host with numbers in Unicode segment', () {
+      const url = 'https://bücher123.de';
+      const expected = 'https://xn--bcher123-65a.de';
+      expect(punycodeEncodeUrl(url), equals(expected));
+    });
+
+    test('should encode the domain of the original issue poster :)', () {
+      const url = 'https://фото.большойчлен.рф/';
+      const expected = 'https://xn--n1aalg.xn--90ailhbncb6fh7b.xn--p1ai/';
+      expect(punycodeEncodeUrl(url), expected);
+    });
+  });
+
+  group('punycodeDecodeUrl', () {
+    test('should return null for null input', () {
+      expect(punycodeDecodeUrl(null), isNull);
+    });
+
+    test('should return null for an invalid URL', () {
+      // "not a url" should fail to parse.
+      expect(punycodeDecodeUrl('not a url'), isNull);
+    });
+
+    test('should return null for a URL with empty host', () {
+      // "https://" is a valid scheme but with no host.
+      expect(punycodeDecodeUrl('https://'), isNull);
+    });
+
+    test('should return ASCII-only URL unchanged', () {
+      const url = 'https://example.com';
+      expect(punycodeDecodeUrl(url), equals(url));
+    });
+
+    test('should decode a single-segment Punycode domain', () {
+      const input = 'https://xn--bcher-kva.de';
+      const expected = 'https://bücher.de';
+      expect(punycodeDecodeUrl(input), equals(expected));
+    });
+
+    test('should decode a multi-segment Punycode domain', () {
+      const input = 'https://shop.xn--bcher-kva.de';
+      const expected = 'https://shop.bücher.de';
+      expect(punycodeDecodeUrl(input), equals(expected));
+    });
+
+    test('should decode URL with port', () {
+      const input = 'https://xn--bcher-kva.de:8080';
+      const expected = 'https://bücher.de:8080';
+      expect(punycodeDecodeUrl(input), equals(expected));
+    });
+
+    test('should decode domains with uppercase punycode prefix correctly', () {
+      const input = 'https://XN--BCHER-KVA.de';
+      const expected = 'https://bücher.de';
+      expect(punycodeDecodeUrl(input), equals(expected));
+    });
+
+    test('should handle mixed segments with no punycode in some parts', () {
+      const input = 'https://news.xn--bcher-kva.de';
+      const expected = 'https://news.bücher.de';
+      expect(punycodeDecodeUrl(input), equals(expected));
+    });
+
+    test('should decode the domain of the original issue poster :)', () {
+      const url = 'https://xn--n1aalg.xn--90ailhbncb6fh7b.xn--p1ai/';
+      const expected = 'https://фото.большойчлен.рф/';
+      expect(punycodeDecodeUrl(url), expected);
+    });
+  });
+}