diff --git a/core/modules/comment/config/optional/views.view.comment.yml b/core/modules/comment/config/optional/views.view.comment.yml
index 36ab4560168b51b432376b7ff8c302ab8395fc46..45d566636fedca71bb3c8c3c5618c76b8b765668 100644
--- a/core/modules/comment/config/optional/views.view.comment.yml
+++ b/core/modules/comment/config/optional/views.view.comment.yml
@@ -806,6 +806,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: changed
           granularity: second
           entity_type: comment
           entity_field: changed
diff --git a/core/modules/comment/config/optional/views.view.comments_recent.yml b/core/modules/comment/config/optional/views.view.comments_recent.yml
index 0387a60737de5610a82b07d505a7449420f2a016..587040dad863a87311407c630ab8aac2c95f76d3 100644
--- a/core/modules/comment/config/optional/views.view.comments_recent.yml
+++ b/core/modules/comment/config/optional/views.view.comments_recent.yml
@@ -206,6 +206,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           plugin_id: date
           entity_type: comment
           entity_field: created
@@ -218,6 +219,9 @@ display:
           admin_label: ''
           order: DESC
           exposed: false
+          expose:
+            label: ''
+            field_identifier: cid
           plugin_id: field
           entity_type: comment
           entity_field: cid
diff --git a/core/modules/dblog/config/optional/views.view.watchdog.yml b/core/modules/dblog/config/optional/views.view.watchdog.yml
index e6542aa9d44c246be2f65ea8da13ad4990030674..b3da477847b6530d8b260ba6b526be70b49c98d7 100644
--- a/core/modules/dblog/config/optional/views.view.watchdog.yml
+++ b/core/modules/dblog/config/optional/views.view.watchdog.yml
@@ -647,6 +647,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: wid
           plugin_id: standard
       title: 'Recent log messages'
       header: {  }
diff --git a/core/modules/media/config/optional/views.view.media.yml b/core/modules/media/config/optional/views.view.media.yml
index 17518dc84a502b470c60450c5096457887521a0c..df4df46c72c970cc93abddd28e6d70e017015c0b 100644
--- a/core/modules/media/config/optional/views.view.media.yml
+++ b/core/modules/media/config/optional/views.view.media.yml
@@ -845,6 +845,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
       title: Media
       header: {  }
diff --git a/core/modules/media_library/config/install/views.view.media_library.yml b/core/modules/media_library/config/install/views.view.media_library.yml
index 35fd413df8d05d70a8c3e91dee9a6a12c4e885f1..1bd04639ef33b879fde2b357ee8acf0d7c6b037c 100644
--- a/core/modules/media_library/config/install/views.view.media_library.yml
+++ b/core/modules/media_library/config/install/views.view.media_library.yml
@@ -418,6 +418,7 @@ display:
           exposed: true
           expose:
             label: 'Newest first'
+            field_identifier: created
           granularity: second
           entity_type: media
           entity_field: created
@@ -433,6 +434,7 @@ display:
           exposed: true
           expose:
             label: 'Name (A-Z)'
+            field_identifier: name
           entity_type: media
           entity_field: name
           plugin_id: standard
@@ -447,6 +449,7 @@ display:
           exposed: true
           expose:
             label: 'Name (Z-A)'
+            field_identifier: name_1
           entity_type: media
           entity_field: name
           plugin_id: standard
diff --git a/core/modules/node/config/optional/views.view.archive.yml b/core/modules/node/config/optional/views.view.archive.yml
index b8e55476d14f0dfa69d4b3a0a17413d60458ac58..9ac4c063b2267e99042db20508b2f1e5a8158aad 100644
--- a/core/modules/node/config/optional/views.view.archive.yml
+++ b/core/modules/node/config/optional/views.view.archive.yml
@@ -77,6 +77,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
           entity_type: node
           entity_field: created
diff --git a/core/modules/node/config/optional/views.view.content_recent.yml b/core/modules/node/config/optional/views.view.content_recent.yml
index 44de40b828f6dd47ff9259c9840c5ce9375f02fd..ec80480d247cfe0752ab16db6ac6f039df672265 100644
--- a/core/modules/node/config/optional/views.view.content_recent.yml
+++ b/core/modules/node/config/optional/views.view.content_recent.yml
@@ -254,6 +254,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: changed
           granularity: second
           entity_type: node
           entity_field: changed
diff --git a/core/modules/node/config/optional/views.view.frontpage.yml b/core/modules/node/config/optional/views.view.frontpage.yml
index efdae12c1860677c584531165b4e1db98550f243..3b0bd89412971f1d6f123c9f2fa097bda6a9e27b 100644
--- a/core/modules/node/config/optional/views.view.frontpage.yml
+++ b/core/modules/node/config/optional/views.view.frontpage.yml
@@ -203,6 +203,7 @@ display:
           admin_label: ''
           expose:
             label: ''
+            field_identifier: sticky
           exposed: false
           field: sticky
           group_type: group
@@ -225,6 +226,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
           entity_type: node
           entity_field: created
diff --git a/core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml b/core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml
index 895019632e4e35a64dec87ea0a123e8731041027..0fa147dda59f1b1dcb37d61e3eaace5632c74ee8 100644
--- a/core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml
+++ b/core/modules/taxonomy/config/optional/views.view.taxonomy_term.yml
@@ -77,6 +77,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: sticky
         created:
           id: created
           table: taxonomy_index
@@ -89,6 +90,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
       arguments:
         tid:
diff --git a/core/modules/user/config/optional/views.view.user_admin_people.yml b/core/modules/user/config/optional/views.view.user_admin_people.yml
index 46a22eba1f37b026c10327e7ed2c37ca0c784aeb..7a6de5a7f3a002b6ec941f125efa28c51a035718 100644
--- a/core/modules/user/config/optional/views.view.user_admin_people.yml
+++ b/core/modules/user/config/optional/views.view.user_admin_people.yml
@@ -846,6 +846,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
           plugin_id: date
           entity_type: user
diff --git a/core/modules/user/config/optional/views.view.who_s_new.yml b/core/modules/user/config/optional/views.view.who_s_new.yml
index 2898850ce67a4e05b06551be201fc2e08bf9c7c2..1054052d3aa779a7dd0fb650873d6a84d8a23d84 100644
--- a/core/modules/user/config/optional/views.view.who_s_new.yml
+++ b/core/modules/user/config/optional/views.view.who_s_new.yml
@@ -156,6 +156,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
           plugin_id: date
           entity_type: user
diff --git a/core/modules/user/config/optional/views.view.who_s_online.yml b/core/modules/user/config/optional/views.view.who_s_online.yml
index 2229485cd970b3e9ef0b56841801d40c4240c0b4..2b40ccf43d7eae225fd08e4d2667e0aee2ead06d 100644
--- a/core/modules/user/config/optional/views.view.who_s_online.yml
+++ b/core/modules/user/config/optional/views.view.who_s_online.yml
@@ -165,6 +165,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: access
           granularity: second
           plugin_id: date
           entity_type: user
diff --git a/core/modules/views/config/schema/views.data_types.schema.yml b/core/modules/views/config/schema/views.data_types.schema.yml
index 708e12f6d8ecb3ffa9e1957b151ba5948ebf51c2..f9f73973f2dab23a233ba7eab66fc71752a7f904 100644
--- a/core/modules/views/config/schema/views.data_types.schema.yml
+++ b/core/modules/views/config/schema/views.data_types.schema.yml
@@ -281,6 +281,9 @@ views_sort_expose:
     label:
       type: label
       label: 'Label'
+    field_identifier:
+      type: string
+      label: 'Field identifier'
 
 views_area:
   type: views_handler
diff --git a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
index cfb593d6e663671f2299ad79b57373f511c850f3..75daffccb0541442709e54b5090202a230727067 100644
--- a/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
+++ b/core/modules/views/src/Plugin/views/exposed_form/ExposedFormPluginBase.php
@@ -157,19 +157,17 @@ public function query() {
     if (!empty($sort_by)) {
       // Make sure the original order of sorts is preserved
       // (e.g. a sticky sort is often first)
-      if (isset($view->sort[$sort_by])) {
-        $view->query->orderby = [];
-        foreach ($view->sort as $key => $sort) {
-          if (!$sort->isExposed()) {
-            $sort->query();
-          }
-          elseif ($key == $sort_by) {
-            if (isset($exposed_data['sort_order']) && in_array($exposed_data['sort_order'], ['ASC', 'DESC'])) {
-              $sort->options['order'] = $exposed_data['sort_order'];
-            }
-            $sort->setRelationship();
-            $sort->query();
+      $view->query->orderby = [];
+      foreach ($view->sort as $key => $sort) {
+        if (!$sort->isExposed()) {
+          $sort->query();
+        }
+        elseif (!empty($sort->options['expose']['field_identifier']) && $sort->options['expose']['field_identifier'] === $sort_by) {
+          if (isset($exposed_data['sort_order']) && in_array($exposed_data['sort_order'], ['ASC', 'DESC'], TRUE)) {
+            $sort->options['order'] = $exposed_data['sort_order'];
           }
+          $sort->setRelationship();
+          $sort->query();
         }
       }
     }
@@ -205,16 +203,18 @@ public function exposedFormAlter(&$form, FormStateInterface $form_state) {
 
     // Check if there is exposed sorts for this view
     $exposed_sorts = [];
+    $exposed_sorts_options = [];
     foreach ($this->view->sort as $id => $handler) {
-      if ($handler->canExpose() && $handler->isExposed()) {
-        $exposed_sorts[$id] = $handler->options['expose']['label'];
+      if ($handler->canExpose() && $handler->isExposed() && !empty($handler->options['expose']['field_identifier'])) {
+        $exposed_sorts[$handler->options['expose']['field_identifier']] = $id;
+        $exposed_sorts_options[$handler->options['expose']['field_identifier']] = $handler->options['expose']['label'];
       }
     }
 
     if (count($exposed_sorts)) {
       $form['sort_by'] = [
         '#type' => 'select',
-        '#options' => $exposed_sorts,
+        '#options' => $exposed_sorts_options,
         '#title' => $this->options['exposed_sorts_label'],
       ];
       $sort_order = [
@@ -222,8 +222,8 @@ public function exposedFormAlter(&$form, FormStateInterface $form_state) {
         'DESC' => $this->options['sort_desc_label'],
       ];
       $user_input = $form_state->getUserInput();
-      if (isset($user_input['sort_by']) && isset($this->view->sort[$user_input['sort_by']])) {
-        $default_sort_order = $this->view->sort[$user_input['sort_by']]->options['order'];
+      if (isset($user_input['sort_by']) && isset($exposed_sorts[$user_input['sort_by']]) && isset($this->view->sort[$exposed_sorts[$user_input['sort_by']]])) {
+        $default_sort_order = $this->view->sort[$exposed_sorts[$user_input['sort_by']]]->options['order'];
       }
       else {
         $first_sort = reset($this->view->sort);
diff --git a/core/modules/views/src/Plugin/views/sort/SortPluginBase.php b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php
index cf036fc810f3b4addf1e179b3ae29145c45fb3f6..fcac77e013e3c4463cd9ed0e41a0a41079b50f71 100644
--- a/core/modules/views/src/Plugin/views/sort/SortPluginBase.php
+++ b/core/modules/views/src/Plugin/views/sort/SortPluginBase.php
@@ -49,6 +49,7 @@ protected function defineOptions() {
     $options['expose'] = [
       'contains' => [
         'label' => ['default' => ''],
+        'field_identifier' => ['default' => ''],
       ],
     ];
     return $options;
@@ -208,7 +209,50 @@ public function buildExposeForm(&$form, FormStateInterface $form_state) {
       '#required' => TRUE,
       '#size' => 40,
       '#weight' => -1,
-   ];
+    ];
+
+    $form['expose']['field_identifier'] = [
+      '#type' => 'textfield',
+      '#default_value' => $this->options['expose']['field_identifier'],
+      '#title' => $this->t('Sort field identifier'),
+      '#required' => TRUE,
+      '#size' => 40,
+      '#description' => $this->t("This will appear in the URL after the ?, as value of 'sort_by' parameter, to identify this sort field. Cannot be blank. Only letters, digits and the dot ('.'), hyphen ('-'), underscore ('_'), and tilde ('~') characters are allowed."),
+    ];
+  }
+
+  /**
+   * Validate the options form.
+   */
+  public function validateExposeForm($form, FormStateInterface $form_state) {
+    $field_identifier = $form_state->getValue([
+      'options',
+      'expose',
+      'field_identifier',
+    ]);
+    if (!preg_match('/^[a-zA-z][a-zA-Z0-9_~.\-]*$/', $field_identifier)) {
+      $form_state->setErrorByName('expose][field_identifier', $this->t('This identifier has illegal characters.'));
+      return;
+    }
+
+    // Validate that the sort field identifier is unique within the sort
+    // handlers. Note that the sort field identifier is different that other
+    // identifiers because it is used as a query string value of the 'sort_by'
+    // parameter, while the others are used as query string parameter keys.
+    // Therefore we can have a sort field identifier be the same as an exposed
+    // filter identifier. This prevents us from using
+    // DisplayPluginInterface::isIdentifierUnique() to test for uniqueness.
+    // @see \Drupal\views\Plugin\views\display\DisplayPluginInterface::isIdentifierUnique()
+    foreach ($this->view->display_handler->getHandlers('sort') as $key => $handler) {
+      if ($handler->canExpose() && $handler->isExposed()) {
+        if ($form_state->get('id') !== $key && isset($handler->options['expose']['field_identifier']) && $field_identifier === $handler->options['expose']['field_identifier']) {
+          $form_state->setErrorByName('expose][field_identifier', $this->t('This identifier is already used by %label sort handler.', [
+            '%label' => $handler->adminLabel(TRUE),
+          ]));
+          return;
+        }
+      }
+    }
   }
 
   /**
@@ -226,6 +270,7 @@ public static function trustedCallbacks() {
   public function defaultExposeOptions() {
     $this->options['expose'] = [
       'label' => $this->definition['title'],
+      'field_identifier' => $this->options['id'],
     ];
   }
 
diff --git a/core/modules/views/src/ViewsConfigUpdater.php b/core/modules/views/src/ViewsConfigUpdater.php
index 88442d46ddc158f4ab730661bb92584c1a868c8b..dfb89e338adb863468a4d2cca032bc2ac6855786 100644
--- a/core/modules/views/src/ViewsConfigUpdater.php
+++ b/core/modules/views/src/ViewsConfigUpdater.php
@@ -138,6 +138,9 @@ public function updateAll(ViewEntityInterface $view) {
       if ($this->processMultivalueBaseFieldHandler($handler, $handler_type, $key, $display_id, $view)) {
         $changed = TRUE;
       }
+      if ($this->processSortFieldIdentifierUpdateHandler($handler, $handler_type)) {
+        $changed = TRUE;
+      }
       return $changed;
     });
   }
@@ -477,4 +480,38 @@ protected function mapOperatorFromSingleToMultiple($single_operator) {
     }
   }
 
+  /**
+   * Updates the sort handlers by adding default sort field identifiers.
+   *
+   * @param \Drupal\views\ViewEntityInterface $view
+   *   The View to update.
+   *
+   * @return bool
+   *   Whether the view was updated.
+   */
+  public function needsSortFieldIdentifierUpdate(ViewEntityInterface $view): bool {
+    return $this->processDisplayHandlers($view, TRUE, function (array &$handler, string $handler_type): bool {
+      return $this->processSortFieldIdentifierUpdateHandler($handler, $handler_type);
+    });
+  }
+
+  /**
+   * Processes sort handlers by adding the sort identifier.
+   *
+   * @param array $handler
+   *   A display handler.
+   * @param string $handler_type
+   *   The handler type.
+   *
+   * @return bool
+   *   Whether the handler was updated.
+   */
+  protected function processSortFieldIdentifierUpdateHandler(array &$handler, string $handler_type): bool {
+    if ($handler_type === 'sort' && !isset($handler['expose']['field_identifier'])) {
+      $handler['expose']['field_identifier'] = $handler['id'];
+      return TRUE;
+    }
+    return FALSE;
+  }
+
 }
diff --git a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
index 676d976afbca44c81bca9a65263d2f38c4312707..42f5ae084caf6d049d66bde1c639a44f4a6b3134 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ExposedFormTest.php
@@ -368,18 +368,24 @@ public function testExposedSortAndItemsPerPage() {
     $this->assertCacheContexts($contexts);
     $this->assertIds(range(40, 16, 1));
 
-    // Change the label to something with special characters.
     $view = Views::getView('test_exposed_form_sort_items_per_page');
     $view->setDisplay();
     $sorts = $view->display_handler->getOption('sorts');
+    // Change the label to something with special characters.
     $sorts['id']['expose']['label'] = $expected_label = "<script>alert('unsafe&dangerous');</script>";
+    // Use a custom sort field identifier.
+    $sorts['id']['expose']['field_identifier'] = $field_identifier = $this->randomMachineName() . '-_.~';
     $view->display_handler->setOption('sorts', $sorts);
     $view->save();
 
+    // Test label escaping.
     $this->drupalGet('test_exposed_form_sort_items_per_page');
     $options = $this->assertSession()->selectExists('edit-sort-by')->findAll('css', 'option');
     $this->assertCount(1, $options);
-    $this->assertSession()->optionExists('edit-sort-by', $expected_label);
+    // Check option existence by option label.
+    $this->assertSession()->optionExists('Sort by', $expected_label);
+    // Check option existence by option value.
+    $this->assertSession()->optionExists('Sort by', $field_identifier);
     $escape_1 = Html::escape($expected_label);
     $escape_2 = Html::escape($escape_1);
     // Make sure we see the single-escaped string in the raw output.
@@ -388,6 +394,13 @@ public function testExposedSortAndItemsPerPage() {
     $this->assertNoRaw($escape_2);
     // And not the raw label, either.
     $this->assertNoRaw($expected_label);
+
+    // Check that the custom field identifier is used in the URL query string.
+    $this->submitForm(['sort_order' => 'DESC'], 'Apply');
+    $this->assertCacheContexts($contexts);
+    $this->assertIds(range(50, 41));
+    $url = $this->getSession()->getCurrentUrl();
+    $this->assertStringContainsString('sort_by=' . urlencode($field_identifier), $url);
   }
 
   /**
diff --git a/core/modules/views/tests/src/Functional/Update/ViewsSortIdentifiersUpdateTest.php b/core/modules/views/tests/src/Functional/Update/ViewsSortIdentifiersUpdateTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e7b0cbdcd30cc1b03abef73d1fc53f13c6619ae8
--- /dev/null
+++ b/core/modules/views/tests/src/Functional/Update/ViewsSortIdentifiersUpdateTest.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Drupal\Tests\views\Functional\Update;
+
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * Tests the views_post_update_sort_identifier() post update.
+ *
+ * @group views
+ * @group legacy
+ */
+class ViewsSortIdentifiersUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.8.0.bare.standard.php.gz',
+    ];
+  }
+
+  /**
+   * Tests views_post_update_sort_identifier().
+   *
+   * @see views_post_update_sort_identifier()
+   */
+  public function testSortIdentifierPostUpdate(): void {
+    $config_factory = \Drupal::configFactory();
+    $view = $config_factory->get('views.view.comments_recent');
+    $trail = 'display.default.display_options.sorts.created';
+    $this->assertArrayNotHasKey('field_identifier', $view->get("{$trail}.expose"));
+
+    $this->runUpdates();
+
+    $view = $config_factory->get('views.view.comments_recent');
+    $sort_handler = $view->get($trail);
+    $this->assertSame($sort_handler['id'], $sort_handler['expose']['field_identifier']);
+  }
+
+}
diff --git a/core/modules/views/tests/src/Kernel/Plugin/DisplayKernelTest.php b/core/modules/views/tests/src/Kernel/Plugin/DisplayKernelTest.php
index a2eb682d4cddcaab8976ee1c1963861f8f23d68c..1fe28612385b4f4e392ec67dd3837f38ed8bdc5c 100644
--- a/core/modules/views/tests/src/Kernel/Plugin/DisplayKernelTest.php
+++ b/core/modules/views/tests/src/Kernel/Plugin/DisplayKernelTest.php
@@ -130,7 +130,10 @@ public function testisIdentifierUnique() {
         'table' => 'views_test_data',
         'plugin_id' => 'standard',
         'order' => 'asc',
-        'expose' => ['label' => 'id'],
+        'expose' => [
+          'label' => 'Id',
+          'field_identifier' => 'name',
+        ],
         'exposed' => TRUE,
       ],
     ];
@@ -156,10 +159,16 @@ public function testisIdentifierUnique() {
     ];
     $view->display_handler->setOption('sorts', $sorts);
     $view->display_handler->setOption('filters', $filters);
-    $view->save();
 
     $this->assertTrue($view->display_handler->isIdentifierUnique('some_id', 'some_id'));
     $this->assertFalse($view->display_handler->isIdentifierUnique('some_id', 'id'));
+
+    // Check that an exposed filter is able to use the same identifier as an
+    // exposed sort.
+    $sorts['name']['expose']['field_identifier'] = 'id';
+    $view->display_handler->handlers = [];
+    $view->display_handler->setOption('sorts', $sorts);
+    $this->assertTrue($view->display_handler->isIdentifierUnique('id', 'id'));
   }
 
 }
diff --git a/core/modules/views/views.post_update.php b/core/modules/views/views.post_update.php
index eab54c113620e6d52cd16e54b6383c90a3295374..cd925a9b845c2b9e50bacffb6d0f3278ab8c6110 100644
--- a/core/modules/views/views.post_update.php
+++ b/core/modules/views/views.post_update.php
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Config\Entity\ConfigEntityUpdater;
+use Drupal\views\ViewEntityInterface;
 use Drupal\views\ViewsConfigUpdater;
 
 /**
@@ -75,3 +76,14 @@ function views_post_update_remove_sorting_global_text_field() {
 function views_post_update_title_translations() {
   \Drupal::service('router.builder')->setRebuildNeeded();
 }
+
+/**
+ * Add the identifier option to all sort handler configurations.
+ */
+function views_post_update_sort_identifier(?array &$sandbox = NULL): void {
+  /** @var \Drupal\views\ViewsConfigUpdater $view_config_updater */
+  $view_config_updater = \Drupal::classResolver(ViewsConfigUpdater::class);
+  \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function (ViewEntityInterface $view) use ($view_config_updater): bool {
+    return $view_config_updater->needsSortFieldIdentifierUpdate($view);
+  });
+}
diff --git a/core/modules/views_ui/css/views_ui.admin.theme.css b/core/modules/views_ui/css/views_ui.admin.theme.css
index 060afc91eee3ba2bab40c627bf6b12a4d533bc46..565132721c6c818bab6c23ee169c56e7a3fb40a4 100644
--- a/core/modules/views_ui/css/views_ui.admin.theme.css
+++ b/core/modules/views_ui/css/views_ui.admin.theme.css
@@ -682,6 +682,7 @@ td.group-title {
 }
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-top: 6px;
   margin-bottom: 6px;
@@ -689,6 +690,7 @@ td.group-title {
 }
 [dir="rtl"] .form-item-options-expose-required,
 [dir="rtl"] .form-item-options-expose-label,
+[dir="rtl"] .form-item-options-expose-field-identifier,
 [dir="rtl"] .form-item-options-expose-description {
   margin-right: 18px;
   margin-left: 0;
diff --git a/core/modules/views_ui/tests/src/Functional/ExposedFormUITest.php b/core/modules/views_ui/tests/src/Functional/ExposedFormUITest.php
index 873125b952e9f588950f6ed85ff948370720f222..62b8ba89da13b12d3747d636fb77c7c742675d7c 100644
--- a/core/modules/views_ui/tests/src/Functional/ExposedFormUITest.php
+++ b/core/modules/views_ui/tests/src/Functional/ExposedFormUITest.php
@@ -105,6 +105,7 @@ public function testExposedAdminUi() {
     $this->drupalGet('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/sort/created');
     $this->helperButtonHasLabel('edit-options-expose-button-button', 'Expose sort');
     $this->assertSession()->fieldNotExists('edit-options-expose-label');
+    $this->assertSession()->fieldNotExists('Sort field identifier');
 
     // Un-expose the filter.
     $this->drupalGet('admin/structure/views/nojs/handler/test_exposed_admin_ui/default/filter/type');
@@ -123,6 +124,7 @@ public function testExposedAdminUi() {
     // Check the label of the expose button.
     $this->helperButtonHasLabel('edit-options-expose-button-button', 'Hide sort');
     $this->assertSession()->fieldValueEquals('edit-options-expose-label', 'Authored on');
+    $this->assertSession()->fieldValueEquals('Sort field identifier', 'created');
 
     // Test adding a new exposed sort criteria.
     $view_id = $this->randomView()['id'];
@@ -135,15 +137,42 @@ public function testExposedAdminUi() {
     $this->submitForm([], 'Expose sort');
     $this->assertSession()->fieldValueEquals('options[order]', 'DESC');
     $this->assertSession()->fieldValueEquals('options[expose][label]', 'Authored on');
-    // Change the label and save the view.
-    $edit = ['options[expose][label]' => $this->randomString()];
+    $this->assertSession()->fieldValueEquals('Sort field identifier', 'created');
+
+    // Change the label and try with an empty identifier.
+    $edit = [
+      'options[expose][label]' => $this->randomString(),
+      'options[expose][field_identifier]' => '',
+    ];
+    $this->submitForm($edit, 'Apply');
+    $this->assertSession()->pageTextContains('Sort field identifier field is required.');
+
+    // Try with an invalid identifier.
+    $edit['options[expose][field_identifier]'] = 'abc&! ###08.';
+    $this->submitForm($edit, 'Apply');
+    $this->assertSession()->pageTextContains('This identifier has illegal characters.');
+
+    // Use a valid identifier.
+    $edit['options[expose][field_identifier]'] = $this->randomMachineName() . '_-~.';
     $this->submitForm($edit, 'Apply');
     $this->submitForm([], 'Save');
+
     // Check that the values were saved.
     $display = View::load($view_id)->getDisplay('default');
     $this->assertTrue($display['display_options']['sorts']['created']['exposed']);
-    $this->assertEquals(['label' => $edit['options[expose][label]']], $display['display_options']['sorts']['created']['expose']);
-    $this->assertEquals('DESC', $display['display_options']['sorts']['created']['order']);
+    $this->assertSame([
+      'label' => $edit['options[expose][label]'],
+      'field_identifier' => $edit['options[expose][field_identifier]'],
+    ], $display['display_options']['sorts']['created']['expose']);
+    $this->assertSame('DESC', $display['display_options']['sorts']['created']['order']);
+
+    // Test the identifier uniqueness.
+    $this->drupalGet("admin/structure/views/nojs/handler/{$view_id}/default/sort/created_1");
+    $this->submitForm([], 'Expose sort');
+    $this->submitForm([
+      'options[expose][field_identifier]' => $edit['options[expose][field_identifier]'],
+    ], 'Apply');
+    $this->assertSession()->pageTextContains('This identifier is already used by Content: Authored on sort handler.');
   }
 
   /**
diff --git a/core/profiles/demo_umami/config/install/views.view.articles_aside.yml b/core/profiles/demo_umami/config/install/views.view.articles_aside.yml
index 7a9f37d8bb5e7b4796fb5c5d7adbc52cfcb85818..b1821bc6e239cabc6a72e996adcf9fc28aa12de8 100644
--- a/core/profiles/demo_umami/config/install/views.view.articles_aside.yml
+++ b/core/profiles/demo_umami/config/install/views.view.articles_aside.yml
@@ -189,6 +189,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
         nid:
           id: nid
@@ -201,6 +202,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: nid
           entity_type: node
           entity_field: nid
           plugin_id: standard
diff --git a/core/profiles/demo_umami/config/install/views.view.featured_articles.yml b/core/profiles/demo_umami/config/install/views.view.featured_articles.yml
index e643082cd3a0234ce402b638dc013314f01440c8..a547a78769f919734a63ff14e8bf78c7abc7dc26 100644
--- a/core/profiles/demo_umami/config/install/views.view.featured_articles.yml
+++ b/core/profiles/demo_umami/config/install/views.view.featured_articles.yml
@@ -202,6 +202,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
         nid:
           id: nid
@@ -214,6 +215,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: nid
           entity_type: node
           entity_field: nid
           plugin_id: standard
diff --git a/core/profiles/demo_umami/config/install/views.view.frontpage.yml b/core/profiles/demo_umami/config/install/views.view.frontpage.yml
index af7dd1167c029d336a0d17e5fb83d318f3c2a198..f614159078d9eed9ed116f6f47373acd0fd4e1b0 100644
--- a/core/profiles/demo_umami/config/install/views.view.frontpage.yml
+++ b/core/profiles/demo_umami/config/install/views.view.frontpage.yml
@@ -229,6 +229,7 @@ display:
           admin_label: ''
           expose:
             label: ''
+            field_identifier: sticky
           exposed: false
           field: sticky
           group_type: group
@@ -251,6 +252,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
           entity_type: node
           entity_field: created
@@ -265,6 +267,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: nid
           entity_type: node
           entity_field: nid
           plugin_id: standard
diff --git a/core/profiles/demo_umami/config/install/views.view.promoted_items.yml b/core/profiles/demo_umami/config/install/views.view.promoted_items.yml
index 2dbaf939619ee3391542a4dde3684a826a3ecb67..7d1262f62c2a4dda88ec0b665fdc8127622096d1 100644
--- a/core/profiles/demo_umami/config/install/views.view.promoted_items.yml
+++ b/core/profiles/demo_umami/config/install/views.view.promoted_items.yml
@@ -220,6 +220,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
       title: 'Promoted Items Double'
       header: {  }
diff --git a/core/profiles/demo_umami/config/install/views.view.recipe_collections.yml b/core/profiles/demo_umami/config/install/views.view.recipe_collections.yml
index 1952fc3c4174a4727d67b0c59d01b785d4de26fc..9113af5ef79974fcd90968e9726ed271b12e0fa1 100644
--- a/core/profiles/demo_umami/config/install/views.view.recipe_collections.yml
+++ b/core/profiles/demo_umami/config/install/views.view.recipe_collections.yml
@@ -178,6 +178,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: name
           entity_type: taxonomy_term
           entity_field: name
           plugin_id: standard
diff --git a/core/profiles/demo_umami/config/install/views.view.recipes.yml b/core/profiles/demo_umami/config/install/views.view.recipes.yml
index e3cdb98cb1c16123f84b420c97d4bcec728c88a5..80af602c9a05dae3e35c276b722475494b16243c 100644
--- a/core/profiles/demo_umami/config/install/views.view.recipes.yml
+++ b/core/profiles/demo_umami/config/install/views.view.recipes.yml
@@ -202,6 +202,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
         nid:
           id: nid
@@ -214,6 +215,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: nid
           entity_type: node
           entity_field: nid
           plugin_id: standard
diff --git a/core/profiles/demo_umami/config/install/views.view.taxonomy_term.yml b/core/profiles/demo_umami/config/install/views.view.taxonomy_term.yml
index 79b65de101d9e5b02432870cbe33c832d35cb618..7b8355922d3a4ad276e6463e0b57cc6d2564c132 100644
--- a/core/profiles/demo_umami/config/install/views.view.taxonomy_term.yml
+++ b/core/profiles/demo_umami/config/install/views.view.taxonomy_term.yml
@@ -77,6 +77,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: sticky
         created:
           id: created
           table: taxonomy_index
@@ -89,6 +90,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
       arguments:
         tid:
diff --git a/core/profiles/demo_umami/config/optional/views.view.media.yml b/core/profiles/demo_umami/config/optional/views.view.media.yml
index 17518dc84a502b470c60450c5096457887521a0c..df4df46c72c970cc93abddd28e6d70e017015c0b 100644
--- a/core/profiles/demo_umami/config/optional/views.view.media.yml
+++ b/core/profiles/demo_umami/config/optional/views.view.media.yml
@@ -845,6 +845,7 @@ display:
           exposed: false
           expose:
             label: ''
+            field_identifier: created
           granularity: second
       title: Media
       header: {  }
diff --git a/core/themes/claro/css/components/views-ui.css b/core/themes/claro/css/components/views-ui.css
index 56fda12d7f482037e325b1b4e8ff2077ac58146e..6fc7a17375873c5a6cbb4f39ae7478b510fd2044 100644
--- a/core/themes/claro/css/components/views-ui.css
+++ b/core/themes/claro/css/components/views-ui.css
@@ -127,12 +127,14 @@ details.fieldset-no-legend {
 
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-left: 1.5em; /* LTR */
 }
 
 [dir="rtl"] .form-item-options-expose-required,
 [dir="rtl"] .form-item-options-expose-label,
+[dir="rtl"] .form-item-options-expose-field-identifier,
 [dir="rtl"] .form-item-options-expose-description {
   margin-right: 1.5em;
   margin-left: 0;
@@ -144,6 +146,7 @@ details.fieldset-no-legend {
 .views-admin-dependent .form-item .form-item,
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-top: 0.375rem;
   margin-bottom: 0.375rem;
diff --git a/core/themes/claro/css/components/views-ui.pcss.css b/core/themes/claro/css/components/views-ui.pcss.css
index 84709902fe7be7325c569d41e3b453f7a0067b09..9e8a723ab059fb912be78277a2871e0618d302bd 100644
--- a/core/themes/claro/css/components/views-ui.pcss.css
+++ b/core/themes/claro/css/components/views-ui.pcss.css
@@ -110,11 +110,13 @@ details.fieldset-no-legend {
  */
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-left: 1.5em; /* LTR */
 }
 [dir="rtl"] .form-item-options-expose-required,
 [dir="rtl"] .form-item-options-expose-label,
+[dir="rtl"] .form-item-options-expose-field-identifier,
 [dir="rtl"] .form-item-options-expose-description {
   margin-right: 1.5em;
   margin-left: 0;
@@ -126,6 +128,7 @@ details.fieldset-no-legend {
 .views-admin-dependent .form-item .form-item,
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-top: 6px;
   margin-bottom: 6px;
diff --git a/core/themes/claro/css/theme/views_ui.admin.theme.css b/core/themes/claro/css/theme/views_ui.admin.theme.css
index b3f21bb4e187575de20adbf9d735c5d544e2d853..76fd7b7de6616455f865e617f672bfd8c193f281 100644
--- a/core/themes/claro/css/theme/views_ui.admin.theme.css
+++ b/core/themes/claro/css/theme/views_ui.admin.theme.css
@@ -663,6 +663,7 @@ td.group-title {
 
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-top: 0.375rem;
   margin-bottom: 0.375rem;
@@ -671,6 +672,7 @@ td.group-title {
 
 [dir="rtl"] .form-item-options-expose-required,
 [dir="rtl"] .form-item-options-expose-label,
+[dir="rtl"] .form-item-options-expose-field-identifier,
 [dir="rtl"] .form-item-options-expose-description {
   margin-right: 1.125rem;
   margin-left: 0;
diff --git a/core/themes/claro/css/theme/views_ui.admin.theme.pcss.css b/core/themes/claro/css/theme/views_ui.admin.theme.pcss.css
index 3b25581b811c20336bee92cac4c93b4be2d4f2b5..3c0ff632501dd6e86332767b7e9423e7e50d4afa 100644
--- a/core/themes/claro/css/theme/views_ui.admin.theme.pcss.css
+++ b/core/themes/claro/css/theme/views_ui.admin.theme.pcss.css
@@ -546,6 +546,7 @@ td.group-title {
 }
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-top: 6px;
   margin-bottom: 6px;
@@ -553,6 +554,7 @@ td.group-title {
 }
 [dir="rtl"] .form-item-options-expose-required,
 [dir="rtl"] .form-item-options-expose-label,
+[dir="rtl"] .form-item-options-expose-field-identifier,
 [dir="rtl"] .form-item-options-expose-description {
   margin-right: 18px;
   margin-left: 0;
diff --git a/core/themes/seven/css/components/views-ui.css b/core/themes/seven/css/components/views-ui.css
index bb79f4d1619b7a83f224fd0fdf54e06de90fe370..912c8257b4cdf3cb16ba3248deb471f67d92b3c4 100644
--- a/core/themes/seven/css/components/views-ui.css
+++ b/core/themes/seven/css/components/views-ui.css
@@ -64,11 +64,13 @@ details.fieldset-no-legend {
  */
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-left: 1.5em; /* LTR */
 }
 [dir="rtl"] .form-item-options-expose-required,
 [dir="rtl"] .form-item-options-expose-label,
+[dir="rtl"] .form-item-options-expose-field-identifier,
 [dir="rtl"] .form-item-options-expose-description {
   margin-right: 1.5em;
   margin-left: 0;
@@ -80,6 +82,7 @@ details.fieldset-no-legend {
 .views-admin-dependent .form-item .form-item,
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-top: 6px;
   margin-bottom: 6px;
diff --git a/core/themes/stable/css/views_ui/views_ui.admin.theme.css b/core/themes/stable/css/views_ui/views_ui.admin.theme.css
index 32de3f97c5f7e191f79d7eef783bb0a94dfa7105..39fc644fea4385631bc5d61de3bed8523a60ce4e 100644
--- a/core/themes/stable/css/views_ui/views_ui.admin.theme.css
+++ b/core/themes/stable/css/views_ui/views_ui.admin.theme.css
@@ -682,6 +682,7 @@ td.group-title {
 }
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-top: 6px;
   margin-bottom: 6px;
@@ -689,6 +690,7 @@ td.group-title {
 }
 [dir="rtl"] .form-item-options-expose-required,
 [dir="rtl"] .form-item-options-expose-label,
+[dir="rtl"] .form-item-options-expose-field-identifier,
 [dir="rtl"] .form-item-options-expose-description {
   margin-right: 18px;
   margin-left: 0;
diff --git a/core/themes/stable9/css/views_ui/views_ui.admin.theme.css b/core/themes/stable9/css/views_ui/views_ui.admin.theme.css
index dd988dfe99d22da98e85a5d00711b4e681e58ac6..344e974012c6e18006b2aea748d447a2b38cff2b 100644
--- a/core/themes/stable9/css/views_ui/views_ui.admin.theme.css
+++ b/core/themes/stable9/css/views_ui/views_ui.admin.theme.css
@@ -682,6 +682,7 @@ td.group-title {
 }
 .form-item-options-expose-required,
 .form-item-options-expose-label,
+.form-item-options-expose-field-identifier,
 .form-item-options-expose-description {
   margin-top: 6px;
   margin-bottom: 6px;
@@ -689,6 +690,7 @@ td.group-title {
 }
 [dir="rtl"] .form-item-options-expose-required,
 [dir="rtl"] .form-item-options-expose-label,
+[dir="rtl"] .form-item-options-expose-field-identifier,
 [dir="rtl"] .form-item-options-expose-description {
   margin-right: 18px;
   margin-left: 0;