diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index 73d84bc4df..b5dc2c16c9 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -206,6 +206,9 @@ field.widget.settings.boolean_checkbox:
     display_label:
       type: boolean
       label: 'Display label'
+    required:
+      type: boolean
+      label: 'Whether or not to make the checkbox required'
 
 field.widget.settings.hidden:
   type: mapping
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/BooleanCheckboxWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/BooleanCheckboxWidget.php
index 660a06460e..198c0a1f87 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/BooleanCheckboxWidget.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/BooleanCheckboxWidget.php
@@ -26,6 +26,7 @@ class BooleanCheckboxWidget extends WidgetBase {
   public static function defaultSettings() {
     return [
       'display_label' => TRUE,
+      'required' => FALSE,
     ] + parent::defaultSettings();
   }
 
@@ -39,6 +40,23 @@ public function settingsForm(array $form, FormStateInterface $form_state) {
       '#default_value' => $this->getSetting('display_label'),
       '#weight' => -1,
     ];
+    // There is no way to distinguish between the "Off" value and no value at
+    // all using a checkbox input. Making a checkbox input required in fact does
+    // not require that "a value" (in other words either the "On" value or the
+    // "Off" value) is input, instead it forces the checkbox to be checked which
+    // corresponds to selecting the "On" value. Therefore we allow the
+    // administrator to choose whether or not the checkbox should be required,
+    // even if the field itself is required.
+    // Strictly speaking the checkbox widget should only be available for
+    // required field definitions in the first place, but enforcing this would
+    // both break existing sites and present a confusing user interface to users
+    // who are not aware of this subtlety.
+    $element['required'] = [
+      '#type' => 'checkbox',
+      '#title' => t('Force users to check the checkbox.'),
+      '#default_value' => $this->getSetting('required'),
+      '#access' => $items = $this->fieldDefinition->isRequired(),
+    ];
     return $element;
   }
 
@@ -51,6 +69,9 @@ public function settingsSummary() {
     $display_label = $this->getSetting('display_label');
     $summary[] = t('Use field label: @display_label', ['@display_label' => ($display_label ? t('Yes') : 'No')]);
 
+    $required = $this->getSetting('required');
+    $summary[] = t('Required: @required', ['@required' => ($required ? t('Yes') : 'No')]);
+
     return $summary;
   }
 
@@ -71,6 +92,10 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       $element['value']['#title'] = $this->fieldDefinition->getSetting('on_label');
     }
 
+    // Allow the administrator to decide whether the checkbox should be
+    // required.
+    $element['value']['#required'] = $this->getSetting('required') && $element['value']['#required'];
+
     return $element;
   }
 
diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php
index da13077de8..ebb3162230 100644
--- a/core/modules/system/system.post_update.php
+++ b/core/modules/system/system.post_update.php
@@ -111,3 +111,26 @@ function system_post_update_change_action_plugins() {
     }
   }
 }
+
+/**
+ * Update form displays to add the 'required' setting to checkbox widgets.
+ */
+  function system_post_update_boolean_checkbox_widget_required_setting() {
+  /* @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
+  foreach (EntityFormDisplay::loadMultiple() as $form_display) {
+    $changed = FALSE;
+    foreach ($form_display->getComponents() as $name => $component) {
+      if (isset($component['type']) && ($component['type'] === 'boolean_checkbox')) {
+        // While the required setting is FALSE by default, setting it to TRUE
+        // matches the previous behavior.
+        $component['settings']['required'] = TRUE;
+        $form_display->setComponent($name, $component);
+        $changed = TRUE;
+      }
+    }
+
+    if ($changed) {
+      $form_display->save();
+    }
+  }
+}
diff --git a/core/modules/system/tests/src/Functional/Update/BooleanCheckboxWidgetSettingsUpdateTest.php b/core/modules/system/tests/src/Functional/Update/BooleanCheckboxWidgetSettingsUpdateTest.php
new file mode 100644
index 0000000000..671702930f
--- /dev/null
+++ b/core/modules/system/tests/src/Functional/Update/BooleanCheckboxWidgetSettingsUpdateTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\Tests\system\Functional\Update;
+
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * Tests upgrading boolean checkbox widget settings.
+ *
+ * @group Update
+ */
+class BooleanCheckboxWidgetSettingsUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [__DIR__ . '/../../../../tests/fixtures/update/drupal-8.bare.standard.php.gz'];
+  }
+
+  /**
+   * Tests upgrading the 'required' setting.
+   *
+   * @see system_post_update_boolean_checkbox_widget_required_setting()
+   */
+  public function testUpdateRequiredSetting() {
+    /* @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
+    $config_factory = $this->container->get('config.factory');
+
+    $get_components_to_update = function() use ($config_factory) {
+      // Gather all components that use the boolean checkbox widget.
+      $updates = [
+        'node.article.default' => ['promote', 'sticky'],
+        'node.page.default' => ['promote', 'sticky'],
+      ];
+
+      $components = [];
+      foreach ($updates as $form_display_id => $component_names) {
+        $form_display_config = $config_factory->get('core.entity_form_display.' . $form_display_id);
+        foreach ($component_names as $component_name) {
+          $components[] = $form_display_config->get('content.' . $component_name);
+        }
+      }
+
+      return $components;
+    };
+
+    foreach ($get_components_to_update() as $component) {
+      $this->assertSame('boolean_checkbox', $component['type']);
+      $this->assertFalse(isset($component['settings']['required']));
+    }
+
+    $this->runUpdates();
+
+    foreach ($get_components_to_update() as $component) {
+      $this->assertSame('boolean_checkbox', $component['type']);
+      $this->assertTrue(isset($component['settings']['required']));
+      $this->assertTrue($component['settings']['required']);
+    }
+  }
+
+}
