diff --git a/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php b/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php
index f2974e3a6361e5815d7b47ef555c77eb972a5bd0..3adb9075ad68013e30dcc373d93737fb26b04b59 100644
--- a/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php
+++ b/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php
@@ -52,6 +52,13 @@ trait SchemaCheckTrait {
         'This value should not be blank.',
       ],
     ],
+    'contact.settings' => [
+      // @todo Simple config cannot have dependencies on any other config.
+      //   Remove this in https://www.drupal.org/project/drupal/issues/3425992.
+      'default_form' => [
+        "The 'contact.form.feedback' config does not exist.",
+      ],
+    ],
     'editor.editor.*' => [
       // @todo Fix stream wrappers not being available early enough in
       //   https://www.drupal.org/project/drupal/issues/3416735
diff --git a/core/modules/contact/config/schema/contact.schema.yml b/core/modules/contact/config/schema/contact.schema.yml
index 4357a426219f5c3e14d82e638eda49f4941814d2..4617e47e7603c71be579d9edb903266d39735ac6 100644
--- a/core/modules/contact/config/schema/contact.schema.yml
+++ b/core/modules/contact/config/schema/contact.schema.yml
@@ -37,20 +37,35 @@ contact.form.*:
 contact.settings:
   type: config_object
   label: 'Contact settings'
+  constraints:
+    FullyValidatable: ~
   mapping:
     default_form:
       type: string
       label: 'Default form identifier'
+      # It is possible to not configure a default form.
+      # @see \Drupal\contact\ContactFormEditForm::save()
+      nullable: true
+      constraints:
+        ConfigExists:
+          prefix: contact.form.
     flood:
+      # @see \Drupal\Core\Flood\FloodInterface::isAllowed()
       type: mapping
       label: 'Flood control'
       mapping:
         limit:
           type: integer
-          label: 'Limit'
+          label: 'Limit (messages per interval)'
+          constraints:
+            Range:
+              min: 1
         interval:
           type: integer
-          label: 'Interval'
+          label: 'Interval (seconds)'
+          constraints:
+            Range:
+              min: 1
     user_default_enabled:
       type: boolean
       label: 'Personal contact form enabled by default'
diff --git a/core/modules/contact/contact.post_update.php b/core/modules/contact/contact.post_update.php
index fcb12edb46a8fef76d30cf43104849ff4d5b03b5..ebdc0d7d0f02d051eb40f556ee8e6ec1ae6105d8 100644
--- a/core/modules/contact/contact.post_update.php
+++ b/core/modules/contact/contact.post_update.php
@@ -13,3 +13,15 @@ function contact_removed_post_updates() {
     'contact_post_update_add_message_redirect_field_to_contact_form' => '9.0.0',
   ];
 }
+
+/**
+ * Converts empty `default_form` in settings to NULL.
+ */
+function contact_post_update_set_empty_default_form_to_null(): void {
+  $config = \Drupal::configFactory()->getEditable('contact.settings');
+  // 'default_form' in 'contact.settings' config must be stored as NULL if it
+  // is empty.
+  if ($config->get('default_form') === '') {
+    $config->set('default_form', NULL)->save();
+  }
+}
diff --git a/core/modules/contact/migrations/d6_contact_settings.yml b/core/modules/contact/migrations/d6_contact_settings.yml
index a91a9fb1bbdf7297fb7f6563dea31307f624ae87..35e64f5591f0174daca50755ff639f3fbf3e9bc4 100644
--- a/core/modules/contact/migrations/d6_contact_settings.yml
+++ b/core/modules/contact/migrations/d6_contact_settings.yml
@@ -11,6 +11,11 @@ source:
 process:
   user_default_enabled: contact_default_status
   'flood/limit': contact_hourly_threshold
+  'flood/interval':
+    plugin: default_value
+    # It was defaulted to 3600 in D6.
+    # @see https://api.drupal.org/api/drupal/includes%21common.inc/function/flood_is_allowed/6.x
+    default_value: 3600
   default_form:
     plugin: migration_lookup
     migration: contact_category
diff --git a/core/modules/contact/migrations/d7_contact_settings.yml b/core/modules/contact/migrations/d7_contact_settings.yml
index 282064630c3e9a66d7e165975f777321ee34137e..166e9c307a27b46e5f0291964b8d47f04cd40f5a 100644
--- a/core/modules/contact/migrations/d7_contact_settings.yml
+++ b/core/modules/contact/migrations/d7_contact_settings.yml
@@ -11,6 +11,11 @@ source:
 process:
   user_default_enabled: contact_default_status
   'flood/limit': contact_threshold_limit
+  'flood/interval':
+    plugin: default_value
+    # It was defaulted to 3600 in D7.
+    # @see https://api.drupal.org/api/drupal/includes%21common.inc/function/flood_is_allowed/7.x
+    default_value: 3600
   default_form:
     plugin: migration_lookup
     migration: contact_category
diff --git a/core/modules/contact/src/Controller/ContactController.php b/core/modules/contact/src/Controller/ContactController.php
index 79ac3f053a09f1fa76833cc98c2541d23dab079a..67b1b98c89b899c7d7344443a8a05f8b9e298657 100644
--- a/core/modules/contact/src/Controller/ContactController.php
+++ b/core/modules/contact/src/Controller/ContactController.php
@@ -50,9 +50,13 @@ public function contactSitePage(ContactFormInterface $contact_form = NULL) {
 
     // Use the default form if no form has been passed.
     if (empty($contact_form)) {
-      $contact_form = $this->entityTypeManager()
-        ->getStorage('contact_form')
-        ->load($config->get('default_form'));
+      $default_form = $config->get('default_form');
+      // Load the default form, if configured.
+      if (!is_null($default_form)) {
+        $contact_form = $this->entityTypeManager()
+          ->getStorage('contact_form')
+          ->load($default_form);
+      }
       // If there are no forms, do not display the form.
       if (empty($contact_form)) {
         if ($this->currentUser()->hasPermission('administer contact forms')) {
diff --git a/core/modules/contact/tests/src/Functional/ContactSitewideTest.php b/core/modules/contact/tests/src/Functional/ContactSitewideTest.php
index fa1c94abf216644bc2e5b7d44ffc2cff67500c49..4b70da6490bc8d728de06a33d10ef64c0b17e07f 100644
--- a/core/modules/contact/tests/src/Functional/ContactSitewideTest.php
+++ b/core/modules/contact/tests/src/Functional/ContactSitewideTest.php
@@ -256,7 +256,7 @@ public function testSiteWideContact() {
 
     // Test contact form with no default form selected.
     $this->config('contact.settings')
-      ->set('default_form', '')
+      ->set('default_form', NULL)
       ->save();
     $this->drupalGet('contact');
     $this->assertSession()->statusCodeEquals(404);
diff --git a/core/modules/contact/tests/src/Functional/Update/NullDefaultFormTest.php b/core/modules/contact/tests/src/Functional/Update/NullDefaultFormTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f825d831c2a857bbefd49f6a76d083358483c3e
--- /dev/null
+++ b/core/modules/contact/tests/src/Functional/Update/NullDefaultFormTest.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\contact\Functional\Update;
+
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * Tests the upgrade path for making 'default_form' in 'contact.settings' config to NULL.
+ *
+ * @group contact
+ * @see contact_post_update_set_empty_default_form_to_null()
+ */
+class NullDefaultFormTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-9.4.0.filled.standard.php.gz',
+    ];
+  }
+
+  /**
+   * Tests the upgrade path for updating empty 'default_form' to NULL.
+   */
+  public function testRunUpdates(): void {
+    $this->config('contact.settings')->set('default_form', '')->save();
+    $this->assertSame('', $this->config('contact.settings')->get('default_form'));
+    $this->runUpdates();
+    $this->assertNull($this->config('contact.settings')->get('default_form'));
+  }
+
+}