- $form['#action'] = $this->url('<current>', [], ['query' => $this->getDestinationArray(), 'external' => FALSE]);
+
+ // Instead of setting an actual action URL, we set the placeholder, which
+ // will be replaced at the very last moment. This ensures forms with
+ // dynamically generated action URLs don't have poor cacheability.
+ // Use the proper API to generate the placeholder, when we have one. See
+ // https://www.drupal.org/node/2562341. The placholder uses a fixed string
+ // that is
+ // Crypt::hashBase64('\Drupal\user\Plugin\Block\UserLoginBlock::build');
+ // This is based on the implementation in
+ // \Drupal\Core\Form\FormBuilder::prepareForm(), but the user login block
+ // requires different behavior for the destination query argument.
+ $placeholder = 'form_action_p_4r8ITd22yaUvXM6SzwrSe9rnQWe48hz9k1Sxto3pBvE';
+
+ $form['#attached']['placeholders'][$placeholder] = [
+ '#lazy_builder' => ['\Drupal\user\Plugin\Block\UserLoginBlock::renderPlaceholderFormAction', []],
+ ];
+ $form['#action'] = $placeholder;
+