diff --git a/core/includes/errors.inc b/core/includes/errors.inc
index 8fc4f39a4c5cc32c79a537ad735a207de17a1901..68e2482d8d10994d55663e4d7efebec283387e3d 100644
--- a/core/includes/errors.inc
+++ b/core/includes/errors.inc
@@ -87,15 +87,21 @@ function _drupal_error_handler_real($error_level, $message, $filename, $line, $c
   // If the site is a test site then fail for user deprecations so they can be
   // caught by the deprecation error handler.
   elseif (DRUPAL_TEST_IN_CHILD_SITE && $error_level === E_USER_DEPRECATED) {
-    $backtrace = debug_backtrace();
-    $caller = Error::getLastCaller($backtrace);
-    _drupal_error_header(
-      Markup::create(Xss::filterAdmin($message)),
-      'User deprecated function',
-      $caller['function'],
-      $caller['file'],
-      $caller['line']
-    );
+    static $seen = [];
+    if (array_search($message, $seen, TRUE) === FALSE) {
+      // Only report each deprecation once. Too many headers can break some
+      // Chrome and web driver testing.
+      $seen[] = $message;
+      $backtrace = debug_backtrace();
+      $caller = Error::getLastCaller($backtrace);
+      _drupal_error_header(
+        Markup::create(Xss::filterAdmin($message)),
+        'User deprecated function',
+        $caller['function'],
+        $caller['file'],
+        $caller['line']
+      );
+    }
   }
 }
 
diff --git a/core/modules/system/tests/modules/test_page_test/src/Controller/Test.php b/core/modules/system/tests/modules/test_page_test/src/Controller/Test.php
index 725c530472d4c11a99c8cfe8637ba25e39a160a7..08ff615510585fb9a1eda9a3ef6703a452ecae00 100644
--- a/core/modules/system/tests/modules/test_page_test/src/Controller/Test.php
+++ b/core/modules/system/tests/modules/test_page_test/src/Controller/Test.php
@@ -155,4 +155,17 @@ public function metaRefresh() {
     return new RedirectResponse(Url::fromRoute('test_page_test.test_page', [], ['absolute' => TRUE])->toString(), 302);
   }
 
+  /**
+   * Returns a page while triggering deprecation notices.
+   */
+  public function deprecations() {
+    // Create 2 identical deprecation messages. This should only trigger a
+    // single response header.
+    @trigger_error('Test deprecation message', E_USER_DEPRECATED);
+    @trigger_error('Test deprecation message', E_USER_DEPRECATED);
+    return [
+      '#markup' => 'Content that triggers deprecation messages',
+    ];
+  }
+
 }
diff --git a/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml b/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml
index 08fa101b7c9b5983e06178307e4da4f369668493..336f6c704ba05286d08d40ff3931f8e8d5a444ee 100644
--- a/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml
+++ b/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml
@@ -114,3 +114,11 @@ test_page_test.meta_refresh:
     _controller: '\Drupal\test_page_test\Controller\Test::metaRefresh'
   requirements:
     _access: 'TRUE'
+
+test_page_test.deprecations:
+  path: '/test-deprecations'
+  defaults:
+    _title: 'Page with deprecation notices'
+    _controller: '\Drupal\test_page_test\Controller\Test::deprecations'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
index fecbb0ca4e6027fbc5f2e535d43d60edac6387c3..b8247ea88f33321f12fabe37eb34f9a9e8ea58e4 100644
--- a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
+++ b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
@@ -743,4 +743,37 @@ public function testEscapingAssertions() {
     $assert->assertNoEscaped("<script>alert('Marked safe');alert(\"Marked safe\");</script>");
   }
 
+  /**
+   * Tests that deprecation headers do not get duplicated.
+   *
+   * @group legacy
+   *
+   * @see \Drupal\Core\Test\HttpClientMiddleware\TestHttpClientMiddleware::__invoke()
+   */
+  public function testDeprecationHeaders() {
+    $this->drupalGet('/test-deprecations');
+
+    $deprecation_messages = [];
+    foreach ($this->getSession()->getResponseHeaders() as $name => $values) {
+      if (preg_match('/^X-Drupal-Assertion-[0-9]+$/', $name, $matches)) {
+        foreach ($values as $value) {
+          // Call \Drupal\simpletest\WebTestBase::error() with the parameters from
+          // the header.
+          $parameters = unserialize(urldecode($value));
+          if (count($parameters) === 3) {
+            if ($parameters[1] === 'User deprecated function') {
+              $deprecation_messages[] = (string) $parameters[0];
+            }
+          }
+        }
+      }
+    }
+
+    $this->assertContains('Test deprecation message', $deprecation_messages);
+    $test_deprecation_messages = array_filter($deprecation_messages, function ($message) {
+      return $message === 'Test deprecation message';
+    });
+    $this->assertCount(1, $test_deprecation_messages);
+  }
+
 }