diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 424b464..c8ab912 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -529,15 +529,11 @@ public function processForm($form_id, &$form, &$form_state) {
 
     // With GET, these forms are always submitted if requested.
     if ($form_state['method'] == 'get' && !empty($form_state['always_process'])) {
-      if (!isset($form_state['input']['form_build_id'])) {
-        $form_state['input']['form_build_id'] = $form['#build_id'];
-      }
       if (!isset($form_state['input']['form_id'])) {
         $form_state['input']['form_id'] = $form_id;
       }
-      if (!isset($form_state['input']['form_token']) && isset($form['#token'])) {
-        $form_state['input']['form_token'] = $this->csrfToken->get($form['#token']);
-      }
+      $form_state['no_cache'] = TRUE;
+      $form['#token'] = NULL;
     }
 
     // self::doBuildForm() finishes building the form by calling element
@@ -678,6 +674,22 @@ public function prepareForm($form_id, &$form, &$form_state) {
     if ($form_state['method'] == 'get' && !isset($form['#method'])) {
       $form['#method'] = 'get';
     }
+    elseif (!isset($form['#method'])) {
+      $form['#method'] = 'post';
+    }
+    // Prevent an undefined index error.
+    if (!isset($form['#token'])) {
+      $form['#token'] = NULL;
+    }
+    // For certain forms like a search for or exposed filters, we want to make
+    // a clean GET request without hidden values as query parameters.
+    if ($form['#method'] == 'get' && !empty($form['#clean_get'])) {
+      $clean_get = TRUE;
+      $form['#token'] = FALSE;
+    }
+    else {
+      $clean_get = FALSE;
+    }
 
     // Generate a new #build_id for this form, if none has been set already.
     // The form_build_id is used as key to cache a particular build of the form.
@@ -688,16 +700,19 @@ public function prepareForm($form_id, &$form, &$form_state) {
     if (!isset($form['#build_id'])) {
       $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
     }
-    $form['form_build_id'] = array(
-      '#type' => 'hidden',
-      '#value' => $form['#build_id'],
-      '#id' => $form['#build_id'],
-      '#name' => 'form_build_id',
-      // Form processing and validation requires this value, so ensure the
-      // submitted form value appears literally, regardless of custom #tree
-      // and #parents being set elsewhere.
-      '#parents' => array('form_build_id'),
-    );
+    // If we are making a clean GET request, omit this hidden element.
+    if (!$clean_get) {
+      $form['form_build_id'] = array(
+        '#type' => 'hidden',
+        '#value' => $form['#build_id'],
+        '#id' => $form['#build_id'],
+        '#name' => 'form_build_id',
+        // Form processing and validation requires this value, so ensure the
+        // submitted form value appears literally, regardless of custom #tree
+        // and #parents being set elsewhere.
+        '#parents' => array('form_build_id'),
+      );
+    }
 
     // Add a token, based on either #token or form_id, to any form displayed to
     // authenticated users. This ensures that any submitted form was actually
@@ -710,7 +725,7 @@ public function prepareForm($form_id, &$form, &$form_state) {
     if ($user && $user->isAuthenticated() && !$form_state['programmed']) {
       // Form constructors may explicitly set #token to FALSE when cross site
       // request forgery is irrelevant to the form, such as search forms.
-      if (isset($form['#token']) && $form['#token'] === FALSE) {
+      if ($form['#token'] === FALSE) {
         unset($form['#token']);
       }
       // Otherwise, generate a public token based on the form id.
@@ -728,7 +743,8 @@ public function prepareForm($form_id, &$form, &$form_state) {
       }
     }
 
-    if (isset($form_id)) {
+    // If we are making a clean GET request, omit this hidden element.
+    if (isset($form_id) && !$clean_get) {
       $form['form_id'] = array(
         '#type' => 'hidden',
         '#value' => $form_id,
diff --git a/core/modules/views/lib/Drupal/views/Form/ViewsExposedForm.php b/core/modules/views/lib/Drupal/views/Form/ViewsExposedForm.php
index ae3da03..11e3b88 100644
--- a/core/modules/views/lib/Drupal/views/Form/ViewsExposedForm.php
+++ b/core/modules/views/lib/Drupal/views/Form/ViewsExposedForm.php
@@ -116,6 +116,10 @@ public function buildForm(array $form, array &$form_state) {
     $form['#action'] = url($view->display_handler->getUrl());
     $form['#theme'] = $view->buildThemeFunctions('views_exposed_form');
     $form['#id'] = drupal_clean_css_identifier('views_exposed_form-' . String::checkPlain($view->storage->id()) . '-' . String::checkPlain($display['id']));
+
+    // Since the exposed form is a GET form, we don't want it to send a wide
+    // variety of information.
+    $form['#clean_get'] = TRUE;
     // $form['#attributes']['class'] = array('views-exposed-form');
 
     /** @var \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase $exposed_form_plugin */
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 5c2c166..1d1a82d 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -907,18 +907,6 @@ function views_pre_render_views_form_views_form($element) {
 }
 
 /**
- * Implement hook_form_alter for the exposed form.
- *
- * Since the exposed form is a GET form, we don't want it to send a wide
- * variety of information.
- */
-function views_form_views_exposed_form_alter(&$form, &$form_state) {
-  $form['form_build_id']['#access'] = FALSE;
-  $form['form_token']['#access'] = FALSE;
-  $form['form_id']['#access'] = FALSE;
-}
-
-/**
  * Implements hook_query_TAG_alter().
  *
  * This is the hook_query_alter() for queries tagged by Views and is used to
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index a1d2ff9..73e7762 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -219,6 +219,193 @@ public function testHandleRedirectWithResponse() {
   }
 
   /**
+   * Tests the prepareForm() method with a post request.
+   *
+   * You can explicitly remove form token checking by using #token = FALSE.
+   */
+  public function testPrepareFormPost() {
+    // The user is anonymous, so the form_token is not emitted.
+    $account = clone $this->account;
+    $account->expects($this->any())
+      ->method('isAuthenticated')
+      ->will($this->returnValue(FALSE));
+    $this->formBuilder->setCurrentUser($account);
+    $form = array();
+    $form_state = $this->formBuilder->getFormStateDefaults();
+    $form = (new TestForm())->buildForm($form, $form_state);
+    $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state);
+
+    $this->assertEquals('post', $form['#method']);
+    $this->assertTrue(isset($form['form_build_id']));
+    $this->assertTrue(isset($form['form_id']));
+    $this->assertFalse(isset($form['form_token']));
+
+    // Now test with a mock authenticated user account. The form_token should
+    // now be present and all three hidden elements render by default, so
+    // the #access key is not set.
+    $account = clone $this->account;
+    $account->expects($this->any())
+      ->method('isAuthenticated')
+      ->will($this->returnValue(TRUE));
+    $this->formBuilder->setCurrentUser($account);
+
+    $form = array();
+    $form_state = $this->formBuilder->getFormStateDefaults();
+    $form = (new TestForm())->buildForm($form, $form_state);
+    $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state);
+
+    $this->assertEquals('post', $form['#method']);
+    $this->assertTrue(isset($form['form_build_id']));
+    $this->assertFalse(isset($form['form_build_id']['#access']));
+    $this->assertTrue(isset($form['form_id']));
+    $this->assertFalse(isset($form['form_id']['#access']));
+    $this->assertTrue(isset($form['form_token']));
+    $this->assertFalse(isset($form['form_token']['#access']));
+
+    // Make sure the form ID and build ID are still set even with no token,
+    // still with an authenticated user.
+    $form = array();
+    $form_state = $this->formBuilder->getFormStateDefaults();
+    $form = (new TestForm())->buildForm($form, $form_state);
+    // Opt out of the token checking.
+    $form['#token'] = FALSE;
+    $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state);
+
+    $this->assertEquals('post', $form['#method']);
+    $this->assertTrue(isset($form['form_build_id']));
+    $this->assertTrue(isset($form['form_id']));
+    $this->assertFalse(isset($form['form_token']));
+  }
+
+  /**
+   * Tests the prepareForm() method with a get request with token checking.
+   */
+  public function testPrepareFormGetWithTokenChecking() {
+    // The user is anonymous, so the form_token is not emitted.
+    $account = clone $this->account;
+    $account->expects($this->any())
+      ->method('isAuthenticated')
+      ->will($this->returnValue(FALSE));
+    $form = array();
+    $form_state = array(
+      'method' => 'get',
+    );
+    $form_state += $this->formBuilder->getFormStateDefaults();
+    $form = (new TestForm())->buildForm($form, $form_state);
+    $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state);
+
+    $this->assertEquals('get', $form['#method']);
+    $this->assertTrue(isset($form['form_build_id']));
+    $this->assertTrue(isset($form['form_id']));
+    $this->assertFalse(isset($form['form_token']));
+
+    // Now test with a mock authenticated user account. The form_token should
+    // now be present and all three hidden elements render by default, so
+    // the #access key is not set.
+    $account = clone $this->account;
+    $account->expects($this->any())
+      ->method('isAuthenticated')
+      ->will($this->returnValue(TRUE));
+    $this->formBuilder->setCurrentUser($account);
+
+    $form = array();
+    $form_state = array(
+      'method' => 'get',
+    );
+    $form_state += $this->formBuilder->getFormStateDefaults();
+    $form = (new TestForm())->buildForm($form, $form_state);
+    $this->formBuilder->prepareForm('my_module_form_id2', $form, $form_state);
+
+    $this->assertEquals('get', $form['#method']);
+    $this->assertTrue(isset($form['form_build_id']));
+    $this->assertFalse(isset($form['form_build_id']['#access']));
+    $this->assertTrue(isset($form['form_id']));
+    $this->assertFalse(isset($form['form_id']['#access']));
+    $this->assertTrue(isset($form['form_token']));
+    $this->assertFalse(isset($form['form_token']['#access']));
+  }
+
+  /**
+   * Tests the prepareForm() method with a get request without token checking.
+   *
+   * You can explicitly remove form token checking by using #clean_get = TRUE
+   */
+  public function testPrepareFormGetWithoutTokenChecking() {
+    // The user is anonymous, so the form_token is not emitted.
+    $account = clone $this->account;
+    $account->expects($this->any())
+      ->method('isAuthenticated')
+      ->will($this->returnValue(FALSE));
+
+    $form = array();
+    $form_state = array(
+      'method' => 'get',
+    );
+    $form_state += $this->formBuilder->getFormStateDefaults();
+    $form = (new TestForm())->buildForm($form, $form_state);
+    // Opt out of the token checking.
+    $form['#clean_get'] = TRUE;
+
+    $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state);
+
+    $this->assertEquals('get', $form['#method']);
+    $this->assertFalse(isset($form['form_build_id']));
+    $this->assertFalse(isset($form['form_id']));
+    $this->assertFalse(isset($form['form_token']));
+
+    // Second variant.
+    $form_state = $this->formBuilder->getFormStateDefaults();
+    $form = (new TestForm())->buildForm($form, $form_state);
+    // Opt out of the token checking.
+    $form['#clean_get'] = TRUE;
+    $form['#method'] = 'get';
+
+    $this->formBuilder->prepareForm('my_module_form_id2', $form, $form_state);
+
+    $this->assertEquals('get', $form['#method']);
+    $this->assertFalse(isset($form['form_build_id']));
+    $this->assertFalse(isset($form['form_id']));
+    $this->assertFalse(isset($form['form_token']));
+
+    // Below is for a mocked authenticated user accout. The form is still
+    // expected to have no form_token because #clean_get = TRUE is set.
+    $account = clone $this->account;
+    $account->expects($this->any())
+      ->method('isAuthenticated')
+      ->will($this->returnValue(TRUE));
+
+    $form = array();
+    $form_state = array(
+      'method' => 'get',
+    );
+    $form_state += $this->formBuilder->getFormStateDefaults();
+    $form = (new TestForm())->buildForm($form, $form_state);
+    // Opt out of the token checking.
+    $form['#clean_get'] = TRUE;
+
+    $this->formBuilder->prepareForm('my_module_form_id', $form, $form_state);
+
+    $this->assertEquals('get', $form['#method']);
+    $this->assertFalse(isset($form['form_build_id']));
+    $this->assertFalse(isset($form['form_id']));
+    $this->assertFalse(isset($form['form_token']));
+
+    // Second variant.
+    $form_state = $this->formBuilder->getFormStateDefaults();
+    $form = (new TestForm())->buildForm($form, $form_state);
+    // Opt out of the token checking.
+    $form['#clean_get'] = TRUE;
+    $form['#method'] = 'get';
+
+    $this->formBuilder->prepareForm('my_module_form_id2', $form, $form_state);
+
+    $this->assertEquals('get', $form['#method']);
+    $this->assertFalse(isset($form['form_build_id']));
+    $this->assertFalse(isset($form['form_id']));
+    $this->assertFalse(isset($form['form_token']));
+  }
+
+  /**
    * Tests the redirectForm() method when a redirect is expected.
    *
    * @param array $form_state
