diff --git a/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php b/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
index 28ad2c04451158ac0b9ea891cca328c47ada2597..78a09a563a5b67755a994edbe53fe3e2bdfdad0a 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ActiveLinkResponseFilter.php
@@ -204,16 +204,15 @@ public static function setLinkActiveClass($html_markup, $current_path, $is_front
       }
       // The query parameters of an active link are equal to the current
       // parameters.
-      if ($add_active) {
-        if ($query) {
-          if (!$node->hasAttribute('data-drupal-link-query') || $node->getAttribute('data-drupal-link-query') !== Json::encode($query)) {
-            $add_active = FALSE;
-          }
-        }
-        else {
-          if ($node->hasAttribute('data-drupal-link-query')) {
-            $add_active = FALSE;
-          }
+      if ($add_active && $node->hasAttribute('data-drupal-link-query')) {
+        $query_match = empty(array_filter(
+          Json::decode($node->getAttribute('data-drupal-link-query')),
+          fn ($value, $key) => $value !== ($query[$key] ?? NULL),
+          ARRAY_FILTER_USE_BOTH
+        ));
+
+        if (!$query_match) {
+          $add_active = FALSE;
         }
       }
 
diff --git a/core/misc/active-link.js b/core/misc/active-link.js
index 464e9a4044f0af4b7391edccd9ec48a623bfbe45..45fdcc25458f8ded715cae702c33a7a4f92e0c4e 100644
--- a/core/misc/active-link.js
+++ b/core/misc/active-link.js
@@ -22,14 +22,10 @@
     attach(context) {
       // Start by finding all potentially active links.
       const path = drupalSettings.path;
-      const queryString = JSON.stringify(path.currentQuery);
-      const querySelector = queryString
-        ? `[data-drupal-link-query="${CSS.escape(queryString)}"]`
-        : ':not([data-drupal-link-query])';
+      path.currentQuery = path.currentQuery ?? [];
       const originalSelectors = [
         `[data-drupal-link-system-path="${CSS.escape(path.currentPath)}"]`,
       ];
-      let selectors;
 
       // If this is the front page, we have to check for the <front> path as
       // well.
@@ -38,7 +34,7 @@
       }
 
       // Add language filtering.
-      selectors = [].concat(
+      const selectors = [].concat(
         // Links without any hreflang attributes (most of them).
         originalSelectors.map((selector) => `${selector}:not([hreflang])`),
         // Links with hreflang equals to the current language.
@@ -47,16 +43,21 @@
         ),
       );
 
-      // Add query string selector for pagers, exposed filters.
-      selectors = selectors.map((current) => current + querySelector);
-
       // Query the DOM.
-      const activeLinks = context.querySelectorAll(selectors.join(','));
-      const il = activeLinks.length;
-      for (let i = 0; i < il; i++) {
-        activeLinks[i].classList.add('is-active');
-        activeLinks[i].setAttribute('aria-current', 'page');
-      }
+      context.querySelectorAll(selectors.join(',')).forEach(function (link) {
+        // Check if the link does not contain query parameters that
+        // don't match the current query.
+        const queryMatch =
+          !link.hasAttribute('data-drupal-link-query') ||
+          !Object.entries(
+            JSON.parse(link.getAttribute('data-drupal-link-query')),
+          ).find(([key, value]) => value !== (path.currentQuery[key] || null));
+
+        if (queryMatch) {
+          link.classList.add('is-active');
+          link.setAttribute('aria-current', 'page');
+        }
+      });
     },
     detach(context, settings, trigger) {
       if (trigger === 'unload') {