diff --git a/core/lib/Drupal/Core/Render/Element/Checkbox.php b/core/lib/Drupal/Core/Render/Element/Checkbox.php
index 9092314dfd..6f75b0482b 100644
--- a/core/lib/Drupal/Core/Render/Element/Checkbox.php
+++ b/core/lib/Drupal/Core/Render/Element/Checkbox.php
@@ -52,7 +52,7 @@ public function getInfo() {
    * {@inheritdoc}
    */
   public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
-    if ($input === FALSE) {
+    if ($input === FALSE || is_null($input)) {
       // Use #default_value as the default value of a checkbox, except change
       // NULL to 0, because FormBuilder::handleInputElement() would otherwise
       // replace NULL with empty string, but an empty string is a potentially
diff --git a/core/lib/Drupal/Core/Render/Element/Checkboxes.php b/core/lib/Drupal/Core/Render/Element/Checkboxes.php
index 35611fd3b9..97f2147259 100644
--- a/core/lib/Drupal/Core/Render/Element/Checkboxes.php
+++ b/core/lib/Drupal/Core/Render/Element/Checkboxes.php
@@ -74,11 +74,20 @@ public static function processCheckboxes(&$element, FormStateInterface $form_sta
         $weight += 0.001;
 
         $element += [$key => []];
+
+        // Detect ajax request.
+        if ($is_ajax = \Drupal::request()->isXmlHttpRequest()) {
+          $default_value = isset($value[$key]) || (is_array($element['#default_value']) && in_array($key, $element['#default_value'])) ? $key : NULL;
+        }
+        else {
+          $default_value = isset($value[$key]) ? $key : NULL;
+        }
+
         $element[$key] += [
           '#type' => 'checkbox',
           '#title' => $choice,
           '#return_value' => $key,
-          '#default_value' => isset($value[$key]) ? $key : NULL,
+          '#default_value' => $default_value,
           '#attributes' => $element['#attributes'],
           '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL,
           // Errors should only be shown on the parent checkboxes element.
