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/book/config/optional/core.entity_form_display.node.book.default.yml b/core/modules/book/config/optional/core.entity_form_display.node.book.default.yml
index b1db6d18a7..37e61d142b 100644
--- a/core/modules/book/config/optional/core.entity_form_display.node.book.default.yml
+++ b/core/modules/book/config/optional/core.entity_form_display.node.book.default.yml
@@ -30,6 +30,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 15
     region: content
     third_party_settings: {  }
@@ -37,6 +38,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 120
     region: content
     third_party_settings: {  }
@@ -44,6 +46,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 16
     region: content
     third_party_settings: {  }
diff --git a/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml b/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml
index 965a691c9e..d979041c37 100644
--- a/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml
+++ b/core/modules/forum/config/optional/core.entity_form_display.node.forum.default.yml
@@ -39,6 +39,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 15
     region: content
     third_party_settings: {  }
@@ -46,6 +47,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 120
     region: content
     third_party_settings: {  }
@@ -53,6 +55,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 16
     region: content
     third_party_settings: {  }
diff --git a/core/modules/node/tests/src/Functional/Update/NodeUpdateTest.php b/core/modules/node/tests/src/Functional/Update/NodeUpdateTest.php
index 223676b31e..0dafea474e 100644
--- a/core/modules/node/tests/src/Functional/Update/NodeUpdateTest.php
+++ b/core/modules/node/tests/src/Functional/Update/NodeUpdateTest.php
@@ -59,8 +59,12 @@ public function testStatusCheckbox() {
      */
     foreach ($form_displays as $id => $form_display) {
       $component = $form_display->getComponent('status');
-      $this->assertEqual('boolean_checkbox', $component['type']);
-      $this->assertEqual(['display_label' => TRUE], $component['settings']);
+      $this->assertSame('boolean_checkbox', $component['type']);
+      $expected_settings = [
+        'display_label' => TRUE,
+        'required' => TRUE,
+      ];
+      $this->assertEquals($expected_settings, $component['settings']);
     }
   }
 
diff --git a/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml b/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml
index ff5f0ec275..6c7b0b2c04 100644
--- a/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml
+++ b/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml
@@ -38,6 +38,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 15
     region: content
     third_party_settings: {  }
@@ -45,6 +46,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 16
     region: content
     third_party_settings: {  }
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php
index cc4cf588a7..ba4db21196 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php
@@ -71,6 +71,7 @@ protected function getExpectedNormalizedEntity() {
           'type' => 'boolean_checkbox',
           'settings' => [
             'display_label' => TRUE,
+            'required' => FALSE,
           ],
           'weight' => 15,
           'region' => 'content',
@@ -82,6 +83,7 @@ protected function getExpectedNormalizedEntity() {
           'region' => 'content',
           'settings' => [
             'display_label' => TRUE,
+            'required' => FALSE,
           ],
           'third_party_settings' => [],
         ],
@@ -89,6 +91,7 @@ protected function getExpectedNormalizedEntity() {
           'type' => 'boolean_checkbox',
           'settings' => [
             'display_label' => TRUE,
+            'required' => FALSE,
           ],
           'weight' => 16,
           'region' => 'content',
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']);
+    }
+  }
+
+}
diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml
index 9082f2d1bd..3a33349bc7 100644
--- a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml
+++ b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml
@@ -63,6 +63,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 15
     region: content
     third_party_settings: {  }
@@ -70,6 +71,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 120
     region: content
     third_party_settings: {  }
@@ -77,6 +79,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 16
     region: content
     third_party_settings: {  }
diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml
index 682f1a550c..31a4d376a0 100644
--- a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml
+++ b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml
@@ -37,6 +37,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 15
     region: content
     third_party_settings: {  }
@@ -44,6 +45,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 120
     region: content
     third_party_settings: {  }
@@ -51,6 +53,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 16
     region: content
     third_party_settings: {  }
diff --git a/core/profiles/standard/config/optional/core.entity_form_display.media.audio.default.yml b/core/profiles/standard/config/optional/core.entity_form_display.media.audio.default.yml
index b3f5f6497f..0ddd24688e 100644
--- a/core/profiles/standard/config/optional/core.entity_form_display.media.audio.default.yml
+++ b/core/profiles/standard/config/optional/core.entity_form_display.media.audio.default.yml
@@ -43,6 +43,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 5
     region: content
     third_party_settings: {  }
diff --git a/core/profiles/standard/config/optional/core.entity_form_display.media.file.default.yml b/core/profiles/standard/config/optional/core.entity_form_display.media.file.default.yml
index 8b4ebe9f3b..2788cbbc13 100644
--- a/core/profiles/standard/config/optional/core.entity_form_display.media.file.default.yml
+++ b/core/profiles/standard/config/optional/core.entity_form_display.media.file.default.yml
@@ -36,6 +36,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 100
     region: content
     third_party_settings: {  }
diff --git a/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml b/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml
index 4488b02eeb..c74fb3132e 100644
--- a/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml
+++ b/core/profiles/standard/config/optional/core.entity_form_display.media.image.default.yml
@@ -38,6 +38,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 100
     region: content
     third_party_settings: {  }
diff --git a/core/profiles/standard/config/optional/core.entity_form_display.media.video.default.yml b/core/profiles/standard/config/optional/core.entity_form_display.media.video.default.yml
index 658648fd18..d7a0b824b4 100644
--- a/core/profiles/standard/config/optional/core.entity_form_display.media.video.default.yml
+++ b/core/profiles/standard/config/optional/core.entity_form_display.media.video.default.yml
@@ -43,6 +43,7 @@ content:
     type: boolean_checkbox
     settings:
       display_label: true
+      required: true
     weight: 5
     region: content
     third_party_settings: {  }
