diff --git a/core/core.services.yml b/core/core.services.yml
index 496a3b0ca70cb17474218784d5d54c7837036ae0..19de5a3af594d9428d4fdb5d4d06510aa9e6c9d6 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -891,6 +891,10 @@ services:
   argument_resolver.default:
     class: Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver
     public: false
+  http_middleware.ajax_page_state:
+    class: Drupal\Core\StackMiddleware\AjaxPageState
+    tags:
+      - { name: http_middleware, priority: 500 }
   http_middleware.negotiation:
     class: Drupal\Core\StackMiddleware\NegotiationMiddleware
     tags:
diff --git a/core/lib/Drupal/Core/Ajax/SettingsCommand.php b/core/lib/Drupal/Core/Ajax/SettingsCommand.php
index ca41720dcb29f7a5a71074b4ac1a8801d77c96f3..9e27073aba34f5f143001d70efecfb3bda0f6005 100644
--- a/core/lib/Drupal/Core/Ajax/SettingsCommand.php
+++ b/core/lib/Drupal/Core/Ajax/SettingsCommand.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Core\Ajax;
 
+use Drupal\Component\Utility\UrlHelper;
+
 /**
  * AJAX command for adjusting Drupal's JavaScript settings.
  *
@@ -53,6 +55,9 @@ public function __construct(array $settings, $merge = FALSE) {
    * Implements Drupal\Core\Ajax\CommandInterface:render().
    */
   public function render() {
+    if (isset($this->settings['ajax_page_state']['libraries'])) {
+      $this->settings['ajax_page_state']['libraries'] = UrlHelper::compressQueryParameter($this->settings['ajax_page_state']['libraries']);
+    }
 
     return [
       'command' => 'settings',
diff --git a/core/lib/Drupal/Core/Asset/AssetResolver.php b/core/lib/Drupal/Core/Asset/AssetResolver.php
index e92102d4e385370350033ddc646ccf2c00d021ea..251b9036f3a5de4bb265531c5b461f5d9b28fbbf 100644
--- a/core/lib/Drupal/Core/Asset/AssetResolver.php
+++ b/core/lib/Drupal/Core/Asset/AssetResolver.php
@@ -4,6 +4,7 @@
 
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
@@ -337,6 +338,11 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize, Language
       // Update the $assets object accordingly, so that it reflects the final
       // settings.
       $assets->setSettings($settings);
+      // Convert ajaxPageState to a compressed string from an array, since it is
+      // used by ajax.js to pass to AJAX requests as a query parameter.
+      if (isset($settings['ajaxPageState']['libraries'])) {
+        $settings['ajaxPageState']['libraries'] = UrlHelper::compressQueryParameter($settings['ajaxPageState']['libraries']);
+      }
       $settings_as_inline_javascript = [
         'type' => 'setting',
         'group' => JS_SETTING,
diff --git a/core/lib/Drupal/Core/StackMiddleware/AjaxPageState.php b/core/lib/Drupal/Core/StackMiddleware/AjaxPageState.php
new file mode 100644
index 0000000000000000000000000000000000000000..060d9253208065df1a86de53e91c7ab1b5304142
--- /dev/null
+++ b/core/lib/Drupal/Core/StackMiddleware/AjaxPageState.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\Core\StackMiddleware;
+
+use Drupal\Component\Utility\UrlHelper;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
+/**
+ * Expands the compressed ajax_page_state query parameter into an array.
+ */
+class AjaxPageState implements HttpKernelInterface {
+
+  /**
+   * Constructs a new AjaxPageState instance.
+   *
+   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $httpKernel
+   *   The wrapped HTTP kernel.
+   */
+  public function __construct(protected readonly HttpKernelInterface $httpKernel) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handle(Request $request, $type = self::MAIN_REQUEST, $catch = TRUE): Response {
+    if ($type === static::MAIN_REQUEST) {
+      if ($request->request->has('ajax_page_state')) {
+        $request->request->set('ajax_page_state', $this->parseAjaxPageState($request->request->all('ajax_page_state')));
+      }
+      elseif ($request->query->has('ajax_page_state')) {
+        $request->query->set('ajax_page_state', $this->parseAjaxPageState($request->query->all('ajax_page_state')));
+      }
+    }
+    return $this->httpKernel->handle($request, $type, $catch);
+  }
+
+  /**
+   * Parse the ajax_page_state variable in the request.
+   *
+   * Decompresses the libraries array key.
+   *
+   * @param array $ajax_page_state
+   *   An array of query parameters, where the libraries parameter is compressed.
+   *
+   * @return array
+   */
+  private function parseAjaxPageState(array $ajax_page_state): array {
+    $ajax_page_state['libraries'] = UrlHelper::uncompressQueryParameter($ajax_page_state['libraries']);
+    return $ajax_page_state;
+  }
+
+}
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index cb8a3dc7d4a751dafcdd49c64498eb75c9bc5835..9f15b2fad4d49d7459ac6b25ba1c2095f51cac74 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -835,6 +835,7 @@
 
     // Allow Drupal to return new JavaScript and CSS files to load without
     // returning the ones already loaded.
+    // @see \Drupal\Core\StackMiddleWare\AjaxPageState
     // @see \Drupal\Core\Theme\AjaxBasePageNegotiator
     // @see \Drupal\Core\Asset\LibraryDependencyResolverInterface::getMinimalRepresentativeSubset()
     // @see system_js_settings_alter()
diff --git a/core/modules/field_ui/tests/src/Functional/FieldTypeCategoriesIntegrationTest.php b/core/modules/field_ui/tests/src/Functional/FieldTypeCategoriesIntegrationTest.php
index 1490c2e8410b44b9b0e20dee366f368b77682ddb..4e076548d238759c6837c93bcc1936f551aa4794 100644
--- a/core/modules/field_ui/tests/src/Functional/FieldTypeCategoriesIntegrationTest.php
+++ b/core/modules/field_ui/tests/src/Functional/FieldTypeCategoriesIntegrationTest.php
@@ -43,17 +43,17 @@ protected function setUp(): void {
    */
   public function testLibrariesLoaded() {
     $this->drupalGet('admin/structure/types/manage/' . $this->drupalCreateContentType()->id() . '/fields/add-field');
-    $page_content = $this->getSession()->getPage()->getContent();
+    $settings = $this->getDrupalSettings();
     $css_libraries = [
-      'drupal.file-icon',
-      'drupal.text-icon',
-      'drupal.options-icon',
-      'drupal.comment-icon',
-      'drupal.link-icon',
+      'file/drupal.file-icon',
+      'text/drupal.text-icon',
+      'options/drupal.options-icon',
+      'comment/drupal.comment-icon',
+      'link/drupal.link-icon',
     ];
+    $libraries = explode(',', $settings['ajaxPageState']['libraries']);
     foreach ($css_libraries as $css_library) {
-      // Check if the library asset is present in the rendered HTML.
-      $this->assertStringContainsString($css_library, $page_content);
+      $this->assertContains($css_library, $libraries);
     }
   }
 
diff --git a/core/modules/media_library/src/MediaLibraryState.php b/core/modules/media_library/src/MediaLibraryState.php
index 7272a616a9d9df951d81a69c29b03ed718f261e1..a9a1310280d34a9c730ffb5b739f6ac32efeea03 100644
--- a/core/modules/media_library/src/MediaLibraryState.php
+++ b/core/modules/media_library/src/MediaLibraryState.php
@@ -114,6 +114,13 @@ public static function fromRequest(Request $request) {
       throw new BadRequestHttpException("Invalid media library parameters specified.");
     }
 
+    // Remove ajax_page_state as it is irrelevant.
+    // @todo: Review other parameters passed
+    // See https://www.drupal.org/project/drupal/issues/3396650
+    if ($query->has('ajax_page_state')) {
+      $query->remove('ajax_page_state');
+    }
+
     // Once we have validated the required parameters, we restore the parameters
     // from the request since there might be additional values.
     $state->replace($query->all());
diff --git a/core/modules/media_library/tests/src/Kernel/MediaLibraryStateTest.php b/core/modules/media_library/tests/src/Kernel/MediaLibraryStateTest.php
index f174f014131416c4f7bebb843cfe13ba97ceea7d..867eb3f68aa01330c7ab48b64b5b094354bbd772 100644
--- a/core/modules/media_library/tests/src/Kernel/MediaLibraryStateTest.php
+++ b/core/modules/media_library/tests/src/Kernel/MediaLibraryStateTest.php
@@ -288,6 +288,9 @@ public function testFromRequest(array $query_overrides, $exception_expected) {
 
     $state = MediaLibraryState::fromRequest(new Request($query));
     $this->assertInstanceOf(MediaLibraryState::class, $state);
+
+    // Assert ajax_page_state is no longer in the state.
+    $this->assertFalse($state->has('ajax_page_state'));
   }
 
   /**
@@ -358,6 +361,12 @@ public function providerFromRequest() {
       TRUE,
     ];
 
+    // Assert ajax_page_state is removed if in the query.
+    $test_data['ajax_page_state'] = [
+      ['ajax_page_state' => 'A long string that gets removed'],
+      FALSE,
+    ];
+
     return $test_data;
   }
 
diff --git a/core/modules/system/tests/src/Functional/Render/AjaxPageStateTest.php b/core/modules/system/tests/src/Functional/Render/AjaxPageStateTest.php
index 005769d38f727fe0ba5e1d9185ff5fb0e4602513..5a9cb6aeba28665f7a31b51b0a883d773b168681 100644
--- a/core/modules/system/tests/src/Functional/Render/AjaxPageStateTest.php
+++ b/core/modules/system/tests/src/Functional/Render/AjaxPageStateTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\system\Functional\Render;
 
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Tests\BrowserTestBase;
 
 /**
@@ -70,7 +71,7 @@ public function testDrupalSettingsIsNotLoaded() {
         "query" =>
           [
             'ajax_page_state' => [
-              'libraries' => 'core/drupalSettings',
+              'libraries' => UrlHelper::compressQueryParameter('core/drupalSettings'),
             ],
           ],
       ]
@@ -89,9 +90,13 @@ public function testDrupalSettingsIsNotLoaded() {
    * comma separated.
    */
   public function testMultipleLibrariesAreNotLoaded() {
-    $this->drupalGet('node',
-      ['query' => ['ajax_page_state' => ['libraries' => 'core/drupal,core/drupalSettings']]]
-    );
+    $this->drupalGet('node', [
+      'query' => [
+        'ajax_page_state' => [
+          'libraries' => UrlHelper::compressQueryParameter('core/drupal,core/drupalSettings'),
+        ],
+      ],
+    ]);
     $this->assertSession()->statusCodeEquals(200);
     // The drupal library from core should be excluded from loading.
     $this->assertSession()->responseNotContains('/core/misc/drupal.js');
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php
index 2866ff39e23d5d5ef7e72e350d40b4d2ef2ae8ce..6f4ac74f006ec06f6f7fa90e98e00328e80d4f68 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\FunctionalJavascriptTests\Ajax;
 
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
 
 /**
@@ -37,7 +38,7 @@ public function testAjaxWithAdminRoute() {
     $assert = $this->assertSession();
     $assert->pageTextContains('Current theme: claro');
 
-    // Now click the modal, which should also use the admin theme.
+    // Now click the modal, which should use the front-end theme.
     $this->drupalGet('ajax-test/dialog');
     $assert->pageTextNotContains('Current theme: stable9');
     $this->clickLink('Link 8 (ajax)');
@@ -59,9 +60,11 @@ public function testDrupalSettingsCachingRegression() {
 
     // Insert a fake library into the already loaded library settings.
     $fake_library = 'fakeLibrary/fakeLibrary';
-    $session->evaluateScript("drupalSettings.ajaxPageState.libraries = drupalSettings.ajaxPageState.libraries + ',$fake_library';");
-
-    $libraries = $session->evaluateScript('drupalSettings.ajaxPageState.libraries');
+    $libraries = $session->evaluateScript("drupalSettings.ajaxPageState.libraries");
+    $libraries = UrlHelper::compressQueryParameter(UrlHelper::uncompressQueryParameter($libraries) . ',' . $fake_library);
+    $session->evaluateScript("drupalSettings.ajaxPageState.libraries = '$libraries';");
+    $ajax_page_state = $session->evaluateScript("drupalSettings.ajaxPageState");
+    $libraries = UrlHelper::uncompressQueryParameter($ajax_page_state['libraries']);
     // Test that the fake library is set.
     $this->assertStringContainsString($fake_library, $libraries);
 
@@ -70,21 +73,22 @@ public function testDrupalSettingsCachingRegression() {
     $assert->assertWaitOnAjaxRequest();
 
     // Test that the fake library is still set after the AJAX call.
-    $libraries = $session->evaluateScript('drupalSettings.ajaxPageState.libraries');
-    $this->assertStringContainsString($fake_library, $libraries);
+    $ajax_page_state = $session->evaluateScript("drupalSettings.ajaxPageState");
+    // Test that the fake library is set.
+    $this->assertStringContainsString($fake_library, UrlHelper::uncompressQueryParameter($ajax_page_state['libraries']));
 
     // Reload the page, this should reset the loaded libraries and remove the
     // fake library.
     $this->drupalGet('ajax-test/dialog');
-    $libraries = $session->evaluateScript('drupalSettings.ajaxPageState.libraries');
-    $this->assertStringNotContainsString($fake_library, $libraries);
+    $ajax_page_state = $session->evaluateScript("drupalSettings.ajaxPageState");
+    $this->assertStringNotContainsString($fake_library, UrlHelper::uncompressQueryParameter($ajax_page_state['libraries']));
 
     // Click on the AJAX link again, and the libraries should still not contain
     // the fake library.
     $this->clickLink('Link 8 (ajax)');
     $assert->assertWaitOnAjaxRequest();
-    $libraries = $session->evaluateScript('drupalSettings.ajaxPageState.libraries');
-    $this->assertStringNotContainsString($fake_library, $libraries);
+    $ajax_page_state = $session->evaluateScript("drupalSettings.ajaxPageState");
+    $this->assertStringNotContainsString($fake_library, UrlHelper::uncompressQueryParameter($ajax_page_state['libraries']));
   }
 
   /**
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php b/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php
index 40e35f6aa9b87c090e9fbbcb1cc3453d2fc12806..222e49cc3889519887f48b7fc429fb067d63d574 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/WebDriverTestBase.php
@@ -3,6 +3,7 @@
 namespace Drupal\FunctionalJavascriptTests;
 
 use Behat\Mink\Exception\DriverException;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Tests\BrowserTestBase;
 use PHPUnit\Runner\BaseTestRunner;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -218,8 +219,11 @@ protected function getDrupalSettings() {
   }
 })();
 EndOfScript;
-
-    return $this->getSession()->evaluateScript($script) ?: [];
+    $settings = $this->getSession()->evaluateScript($script) ?: [];
+    if (isset($settings['ajaxPageState'])) {
+      $settings['ajaxPageState']['libraries'] = UrlHelper::uncompressQueryParameter($settings['ajaxPageState']['libraries']);
+    }
+    return $settings;
   }
 
   /**
diff --git a/core/tests/Drupal/FunctionalTests/Bootstrap/UncaughtExceptionTest.php b/core/tests/Drupal/FunctionalTests/Bootstrap/UncaughtExceptionTest.php
index 2635280d0bf9b73f0750294c64985db39f747e93..cba5665cf14e1e6605bf3d6074b70791739a72ca 100644
--- a/core/tests/Drupal/FunctionalTests/Bootstrap/UncaughtExceptionTest.php
+++ b/core/tests/Drupal/FunctionalTests/Bootstrap/UncaughtExceptionTest.php
@@ -237,7 +237,7 @@ public function testLoggerException() {
 
     // Find fatal error logged to the error.log
     $errors = file(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
-    $this->assertCount(8, $errors, 'The error + the error that the logging service is broken has been written to the error log.');
+    $this->assertCount(10, $errors, 'The error + the error that the logging service is broken has been written to the error log.');
     $this->assertStringContainsString('Failed to log error', $errors[0], 'The error handling logs when an error could not be logged to the logger.');
 
     $expected_path = \Drupal::root() . '/core/modules/system/tests/modules/error_service_test/src/MonkeysInTheControlRoom.php';
diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php
index 3d3e41c51d945c9d6e134ef7d504088115be23bc..38b41f4163d198dd18d1a984ba31eab26f1c1c4f 100644
--- a/core/tests/Drupal/Tests/BrowserTestBase.php
+++ b/core/tests/Drupal/Tests/BrowserTestBase.php
@@ -8,6 +8,7 @@
 use Behat\Mink\Selector\SelectorsHandler;
 use Behat\Mink\Session;
 use Drupal\Component\Serialization\Json;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Test\FunctionalTestSetupTrait;
 use Drupal\Core\Test\TestSetupTrait;
@@ -632,7 +633,11 @@ protected function config($name) {
   protected function getDrupalSettings() {
     $html = $this->getSession()->getPage()->getContent();
     if (preg_match('@<script type="application/json" data-drupal-selector="drupal-settings-json">([^<]*)</script>@', $html, $matches)) {
-      return Json::decode($matches[1]);
+      $settings = Json::decode($matches[1]);
+      if (isset($settings['ajaxPageState']['libraries'])) {
+        $settings['ajaxPageState']['libraries'] = UrlHelper::uncompressQueryParameter($settings['ajaxPageState']['libraries']);
+      }
+      return $settings;
     }
     return [];
   }