diff --git a/web/push-notif/badge-handler.react.js b/web/push-notif/badge-handler.react.js
--- a/web/push-notif/badge-handler.react.js
+++ b/web/push-notif/badge-handler.react.js
@@ -2,35 +2,59 @@
 
 import * as React from 'react';
 
-import { connectionSelector } from 'lib/selectors/keyserver-selectors.js';
-import { unreadCount } from 'lib/selectors/thread-selectors.js';
-import type { ConnectionInfo } from 'lib/types/socket-types.js';
+import { allConnectionInfosSelector } from 'lib/selectors/keyserver-selectors.js';
+import { allUnreadCounts } from 'lib/selectors/thread-selectors.js';
 
-import { authoritativeKeyserverID } from '../authoritative-keyserver.js';
+import {
+  updateNotifsUnreadCountStorage,
+  queryNotifsUnreadCountStorage,
+} from './notif-crypto-utils.js';
 import electron from '../electron.js';
 import { useSelector } from '../redux/redux-utils.js';
 import getTitle from '../title/get-title.js';
 
 function useBadgeHandler() {
-  const connection = useSelector(connectionSelector(authoritativeKeyserverID));
-  const prevConnection = React.useRef<?ConnectionInfo>();
-
-  const boundUnreadCount = useSelector(unreadCount);
-  const prevUnreadCount = React.useRef(boundUnreadCount);
+  const connection = useSelector(allConnectionInfosSelector);
+  const unreadCount = useSelector(allUnreadCounts);
 
   React.useEffect(() => {
-    if (
-      connection?.status === 'connected' &&
-      (prevConnection.current?.status !== 'connected' ||
-        boundUnreadCount !== prevUnreadCount.current)
-    ) {
-      document.title = getTitle(boundUnreadCount);
-      electron?.setBadge(boundUnreadCount === 0 ? null : boundUnreadCount);
-    }
-
-    prevConnection.current = connection;
-    prevUnreadCount.current = boundUnreadCount;
-  }, [boundUnreadCount, connection]);
+    void (async () => {
+      const unreadCountUpdates: {
+        [keyserverID: string]: number,
+      } = {};
+      const unreadCountQueries: Array<string> = [];
+
+      for (const keyserverID in unreadCount) {
+        if (connection[keyserverID]?.status !== 'connected') {
+          unreadCountQueries.push(keyserverID);
+          continue;
+        }
+        unreadCountUpdates[keyserverID] = unreadCount[keyserverID];
+      }
+
+      let queriedUnreadCounts: { +[keyserverID: string]: ?number } = {};
+      [queriedUnreadCounts] = await Promise.all([
+        queryNotifsUnreadCountStorage(unreadCountQueries),
+        updateNotifsUnreadCountStorage(unreadCountUpdates),
+      ]);
+
+      let totalUnreadCount = 0;
+      for (const keyserverID in unreadCountUpdates) {
+        totalUnreadCount += unreadCountUpdates[keyserverID];
+      }
+
+      for (const keyserverID in queriedUnreadCounts) {
+        if (!queriedUnreadCounts[keyserverID]) {
+          totalUnreadCount += unreadCount[keyserverID];
+          continue;
+        }
+        totalUnreadCount += queriedUnreadCounts[keyserverID];
+      }
+
+      document.title = getTitle(totalUnreadCount);
+      electron?.setBadge(totalUnreadCount === 0 ? null : totalUnreadCount);
+    })();
+  }, [unreadCount, connection]);
 }
 
 export default useBadgeHandler;
diff --git a/web/push-notif/notif-crypto-utils.js b/web/push-notif/notif-crypto-utils.js
--- a/web/push-notif/notif-crypto-utils.js
+++ b/web/push-notif/notif-crypto-utils.js
@@ -58,6 +58,8 @@
 const ASHOAT_KEYSERVER_ID_USED_ONLY_FOR_MIGRATION_FROM_LEGACY_NOTIF_STORAGE =
   '256';
 
+const INDEXED_DB_UNREAD_COUNT_SUFFIX = 'unreadCount';
+
 async function decryptWebNotification(
   encryptedNotification: EncryptedWebNotification,
 ): Promise<PlainTextWebNotification | WebNotifDecryptionError> {
@@ -105,6 +107,11 @@
       encryptedPayload,
     );
 
+    const { unreadCount } = decryptedNotification;
+    await updateNotifsUnreadCountStorage({
+      [keyserverID]: unreadCount,
+    });
+
     return { id, ...decryptedNotification };
   } catch (e) {
     return {
@@ -146,8 +153,9 @@
     };
   }
 
+  let decryptedNotification;
   try {
-    return await commonDecrypt(
+    decryptedNotification = await commonDecrypt<{ +[string]: mixed }>(
       encryptedOlmData,
       olmDataContentKey,
       encryptionKey,
@@ -159,6 +167,27 @@
       staffCanSee,
     };
   }
+
+  if (!keyserverID) {
+    return decryptedNotification;
+  }
+
+  // iOS notifications require that unread count is set under
+  // `badge` key. Since MacOS notifications are created by the
+  // same function the unread count is also set under `badge` key
+  const { badge } = decryptedNotification;
+  if (typeof badge === 'number') {
+    await updateNotifsUnreadCountStorage({ [(keyserverID: string)]: badge });
+    return decryptedNotification;
+  }
+
+  const { unreadCount } = decryptedNotification;
+  if (typeof unreadCount === 'number') {
+    await updateNotifsUnreadCountStorage({
+      [(keyserverID: string)]: unreadCount,
+    });
+  }
+  return decryptedNotification;
 }
 
 async function commonDecrypt<T>(
@@ -463,10 +492,53 @@
   await Promise.all([...insertionPromises, ...deletionPromises]);
 }
 
+// Multiple keyserver unread count utilities
+function getKeyserverUnreadCountKey(keyserverID: string) {
+  return [
+    INDEXED_DB_KEYSERVER_PREFIX,
+    keyserverID,
+    INDEXED_DB_UNREAD_COUNT_SUFFIX,
+  ].join(INDEXED_DB_KEY_SEPARATOR);
+}
+
+async function updateNotifsUnreadCountStorage(perKeyserverUnreadCount: {
+  +[keyserverID: string]: number,
+}) {
+  const unreadCountUpdatePromises: Array<Promise<number>> = Object.entries(
+    perKeyserverUnreadCount,
+  ).map(([keyserverID, unreadCount]) => {
+    const keyserverUnreadCountKey = getKeyserverUnreadCountKey(keyserverID);
+    return localforage.setItem<number>(keyserverUnreadCountKey, unreadCount);
+  });
+
+  await Promise.all(unreadCountUpdatePromises);
+}
+
+async function queryNotifsUnreadCountStorage(
+  keyserverIDs: $ReadOnlyArray<string>,
+): Promise<{
+  +[keyserverID: string]: ?number,
+}> {
+  const queryUnreadCountPromises: Array<Promise<[string, ?number]>> =
+    keyserverIDs.map(async keyserverID => {
+      const keyserverUnreadCountKey = getKeyserverUnreadCountKey(keyserverID);
+      const unreadCount = await localforage.getItem<number>(
+        keyserverUnreadCountKey,
+      );
+      return [keyserverID, unreadCount];
+    });
+
+  const queriedUnreadCounts: $ReadOnlyArray<[string, ?number]> =
+    await Promise.all(queryUnreadCountPromises);
+  return Object.fromEntries(queriedUnreadCounts);
+}
+
 export {
   decryptWebNotification,
   decryptDesktopNotification,
   getOlmDataContentKeyForCookie,
   getOlmEncryptionKeyDBLabelForCookie,
   migrateLegacyOlmNotificationsSessions,
+  updateNotifsUnreadCountStorage,
+  queryNotifsUnreadCountStorage,
 };