diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
index 770b96f79e8e347ad9c55461ab8253fd9ff43bc3..2ae21143b2afb438074b245e5162eeebf1cd25f5 100644
--- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
@@ -147,7 +147,12 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $url, $st
         }
 
         $response = $this->httpKernel->handle($sub_request, HttpKernelInterface::SUB_REQUEST);
-        $response->setStatusCode($status_code);
+        // Only 2xx responses should have their status code overridden; any
+        // other status code should be passed on: redirects (3xx), error (5xx)…
+        // @see https://www.drupal.org/node/2603788#comment-10504916
+        if ($response->isSuccessful()) {
+          $response->setStatusCode($status_code);
+        }
 
         // Persist any special HTTP headers that were set on the exception.
         if ($exception instanceof HttpExceptionInterface) {
diff --git a/core/lib/Drupal/Core/EventSubscriber/HtmlResponsePlaceholderStrategySubscriber.php b/core/lib/Drupal/Core/EventSubscriber/HtmlResponsePlaceholderStrategySubscriber.php
index 943b08bd1ee780e4cc646a95ca074e9944dddcef..c67f531cce79a400c58c4331b29ed170bc868432 100644
--- a/core/lib/Drupal/Core/EventSubscriber/HtmlResponsePlaceholderStrategySubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/HtmlResponsePlaceholderStrategySubscriber.php
@@ -48,10 +48,6 @@ public function __construct(PlaceholderStrategyInterface $placeholder_strategy)
    *   The event to process.
    */
   public function onRespond(FilterResponseEvent $event) {
-    if (!$event->isMasterRequest()) {
-      return;
-    }
-
     $response = $event->getResponse();
     if (!$response instanceof HtmlResponse) {
       return;
diff --git a/core/lib/Drupal/Core/EventSubscriber/HtmlResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/HtmlResponseSubscriber.php
index 6fada86d857f7f0c6dc0360e5390a9493c817072..be64602ad8bbd99cc16ade2ce5d4069d3023cdc5 100644
--- a/core/lib/Drupal/Core/EventSubscriber/HtmlResponseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/HtmlResponseSubscriber.php
@@ -42,10 +42,6 @@ public function __construct(AttachmentsResponseProcessorInterface $html_response
    *   The event to process.
    */
   public function onRespond(FilterResponseEvent $event) {
-    if (!$event->isMasterRequest()) {
-      return;
-    }
-
     $response = $event->getResponse();
     if (!$response instanceof HtmlResponse) {
       return;
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 5e2835e6abefea5d7cdfa143827375c5edbb58e7..7c817c2cee180d25c782e58674749ff29b82404b 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
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\test_page_test\Controller;
+use Symfony\Component\HttpKernel\Exception\HttpException;
 
 /**
  * Defines a test controller for page titles.
@@ -75,4 +76,14 @@ public function renderPage() {
     );
   }
 
+  /**
+   * Throws a HTTP exception.
+   *
+   * @param int $code
+   *   The status code.
+   */
+  public function httpResponseException($code) {
+    throw new HttpException($code);
+  }
+
 }
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 240842349af3f3c7673f83892af455e0c2b17fbd..a00d0844cef19516de8bf06201fad869b7658f27 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
@@ -42,3 +42,11 @@ test_page_test.admin_render_title:
     _controller: '\Drupal\test_page_test\Controller\Test::renderTitle'
   requirements:
     _access: 'TRUE'
+
+test_page_test.http_response_exception:
+  path: '/test-http-response-exception/{code}'
+  defaults:
+    _controller: '\Drupal\test_page_test\Controller\Test::httpResponseException'
+    code: 200
+  requirements:
+    _access: 'TRUE'
diff --git a/core/tests/Drupal/KernelTests/RequestProcessing/RedirectOnExceptionTest.php b/core/tests/Drupal/KernelTests/RequestProcessing/RedirectOnExceptionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c7a6860ff172febc8fdfc236cca08309af50304
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/RequestProcessing/RedirectOnExceptionTest.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\KernelTests\RequestProcessing\RedirectOnExceptionTest.
+ */
+
+namespace Drupal\KernelTests\RequestProcessing;
+
+use Drupal\KernelTests\KernelTestBase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Tests redirects on exception pages.
+ *
+ * @group request_processing
+ */
+class RedirectOnExceptionTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'test_page_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installSchema('system', ['router', 'url_alias']);
+    \Drupal::service('router.builder')->rebuild();
+  }
+
+  public function testRedirectOn404() {
+    \Drupal::configFactory()->getEditable('system.site')
+      ->set('page.404', '/test-http-response-exception/' . Response::HTTP_PERMANENTLY_REDIRECT)
+      ->save();
+
+    /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */
+    $http_kernel = \Drupal::service('http_kernel');
+
+    // Foo doesn't exist, so this triggers the 404 page.
+    $request = Request::create('/foo');
+    $response = $http_kernel->handle($request);
+    $this->assertEquals(Response::HTTP_PERMANENTLY_REDIRECT, $response->getStatusCode());
+  }
+
+}