diff --git a/core/modules/big_pipe/big_pipe.services.yml b/core/modules/big_pipe/big_pipe.services.yml
index e1080f084dce0053d127c9fed5dbfe33b0bce024..22d937a3d8ff06e2282213ff8d44035122623ca9 100644
--- a/core/modules/big_pipe/big_pipe.services.yml
+++ b/core/modules/big_pipe/big_pipe.services.yml
@@ -11,7 +11,7 @@ services:
       - { name: placeholder_strategy, priority: 0 }
   big_pipe:
     class: Drupal\big_pipe\Render\BigPipe
-    arguments: ['@renderer', '@session', '@request_stack', '@http_kernel', '@event_dispatcher', '@config.factory', '@messenger']
+    arguments: ['@renderer', '@session', '@request_stack', '@http_kernel', '@event_dispatcher', '@config.factory', '@messenger', '@router.request_context', '@logger.channel.php']
   Drupal\big_pipe\Render\BigPipe: '@big_pipe'
   html_response.attachments_processor.big_pipe:
     public: false
diff --git a/core/modules/big_pipe/src/Render/BigPipe.php b/core/modules/big_pipe/src/Render/BigPipe.php
index f703ebb3645d4787ebd48e0488069c2ded94ab87..ac85ec4b620beac03613304962ed925c95963e42 100644
--- a/core/modules/big_pipe/src/Render/BigPipe.php
+++ b/core/modules/big_pipe/src/Render/BigPipe.php
@@ -2,17 +2,24 @@
 
 namespace Drupal\big_pipe\Render;
 
+use Drupal\Component\HttpFoundation\SecuredRedirectResponse;
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\MessageCommand;
+use Drupal\Core\Ajax\RedirectCommand;
 use Drupal\Core\Ajax\ReplaceCommand;
 use Drupal\Core\Asset\AttachedAssets;
 use Drupal\Core\Asset\AttachedAssetsInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Form\EnforcedResponseException;
 use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Core\Render\HtmlResponse;
 use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Routing\LocalRedirectResponse;
+use Drupal\Core\Routing\RequestContext;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RequestStack;
@@ -173,6 +180,8 @@ public function __construct(
     protected EventDispatcherInterface $eventDispatcher,
     protected ConfigFactoryInterface $configFactory,
     protected MessengerInterface $messenger,
+    protected RequestContext $requestContext,
+    protected LoggerInterface $logger,
   ) {
   }
 
@@ -553,6 +562,53 @@ protected function sendPlaceholders(array $placeholders, array $placeholder_orde
             $cumulative_assets->setAlreadyLoadedLibraries(explode(',', $ajax_response->getAttachments()['drupalSettings']['ajaxPageState']['libraries']));
           }
         }
+        // Handle enforced redirect responses.
+        // A typical use case where this might happen are forms using GET as
+        // #method that are build inside a lazy builder.
+        catch (EnforcedResponseException $e) {
+          $response = $e->getResponse();
+          if (!$response instanceof RedirectResponse) {
+            throw $e;
+          }
+          $ajax_response = new AjaxResponse();
+          if ($response instanceof SecuredRedirectResponse) {
+            // Only redirect to safe locations.
+            $ajax_response->addCommand(new RedirectCommand($response->getTargetUrl()));
+          }
+          else {
+            try {
+              // SecuredRedirectResponse is an abstract class that requires a
+              // concrete implementation. Default to LocalRedirectResponse, which
+              // considers only redirects to within the same site as safe.
+              $safe_response = LocalRedirectResponse::createFromRedirectResponse($response);
+              $safe_response->setRequestContext($this->requestContext);
+              $ajax_response->addCommand(new RedirectCommand($safe_response->getTargetUrl()));
+            }
+            catch (\InvalidArgumentException) {
+              // If the above failed, it's because the redirect target wasn't
+              // local. Do not follow that redirect. Log an error message
+              // instead, then return a 400 response to the client with the
+              // error message. We don't throw an exception, because this is a
+              // client error rather than a server error.
+              $message = 'Redirects to external URLs are not allowed by default, use \Drupal\Core\Routing\TrustedRedirectResponse for it.';
+              $this->logger->error($message);
+              $ajax_response->addCommand(new MessageCommand($message));
+            }
+          }
+          $ajax_response = $this->filterEmbeddedResponse($fake_request, $ajax_response);
+
+          $json = $ajax_response->getContent();
+          $output = <<<EOF
+<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="$placeholder_id">
+$json
+</script>
+EOF;
+          $this->sendChunk($output);
+
+          // Send the stop signal.
+          $this->sendChunk("\n" . static::STOP_SIGNAL . "\n");
+          break;
+        }
         catch (\Exception $e) {
           unset($fibers[$placeholder_id]);
           if ($this->configFactory->get('system.logging')->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) {
diff --git a/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.routing.yml b/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.routing.yml
index 8edbcec487b456f8cfffaf8f1e9c884eaea9a160..a1ecd4612f8f8dd7a93fe8f45a474f7593b62278 100644
--- a/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.routing.yml
+++ b/core/modules/big_pipe/tests/modules/big_pipe_test/big_pipe_test.routing.yml
@@ -31,3 +31,19 @@ big_pipe_test_preview:
     _title: 'Test placeholder previews'
   requirements:
     _access: 'TRUE'
+
+big_pipe_test_trusted_redirect:
+  path: '/big_pipe_test_trusted_redirect'
+  defaults:
+    _controller: '\Drupal\big_pipe_test\BigPipeTestController::trustedRedirectLazyBuilder'
+    _title: 'BigPipe test trusted redirect'
+  requirements:
+    _access: 'TRUE'
+
+big_pipe_test_untrusted_redirect:
+  path: '/big_pipe_test_untrusted_redirect'
+  defaults:
+    _controller: '\Drupal\big_pipe_test\BigPipeTestController::untrustedRedirectLazyBuilder'
+    _title: 'BigPipe test untrusted redirect'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php b/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php
index db4d7c0e9db29b93aa7328ac6404d09f4f2d2639..96592071ee60e676e876c0df1d873ea68d3b3d1f 100644
--- a/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php
+++ b/core/modules/big_pipe/tests/modules/big_pipe_test/src/BigPipeTestController.php
@@ -4,7 +4,9 @@
 
 use Drupal\big_pipe\Render\BigPipeMarkup;
 use Drupal\big_pipe_test\EventSubscriber\BigPipeTestSubscriber;
+use Drupal\Core\Form\EnforcedResponseException;
 use Drupal\Core\Security\TrustedCallbackInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Returns responses for Big Pipe routes.
@@ -213,11 +215,63 @@ public static function counter() {
     ];
   }
 
+  /**
+   * Route callback to test a trusted lazy builder redirect response.
+   *
+   * @return array
+   *   The lazy builder callback.
+   */
+  public function trustedRedirectLazyBuilder(): array {
+    return [
+      'redirect' => [
+        '#lazy_builder' => [static::class . '::redirectTrusted', []],
+        '#create_placeholder' => TRUE,
+      ],
+    ];
+  }
+
+  /**
+   * Supports Big Pipe testing of the enforced redirect response.
+   *
+   * @throws \Drupal\Core\Form\EnforcedResponseException
+   *   Trigger catch of Big Pipe enforced redirect response exception.
+   */
+  public static function redirectTrusted(): void {
+    $response = new RedirectResponse('/big_pipe_test');
+    throw new EnforcedResponseException($response);
+  }
+
+  /**
+   * Route callback to test an untrusted lazy builder redirect response.
+   *
+   * @return array
+   *   The lazy builder callback.
+   */
+  public function untrustedRedirectLazyBuilder(): array {
+    return [
+      'redirect' => [
+        '#lazy_builder' => [static::class . '::redirectUntrusted', []],
+        '#create_placeholder' => TRUE,
+      ],
+    ];
+  }
+
+  /**
+   * Supports Big Pipe testing of an untrusted external URL.
+   *
+   * @throws \Drupal\Core\Form\EnforcedResponseException
+   *   Trigger catch of Big Pipe enforced redirect response exception.
+   */
+  public static function redirectUntrusted(): void {
+    $response = new RedirectResponse('https://example.com');
+    throw new EnforcedResponseException($response);
+  }
+
   /**
    * {@inheritdoc}
    */
   public static function trustedCallbacks() {
-    return ['currentTime', 'piggy', 'helloOrHi', 'exception', 'responseException', 'counter'];
+    return ['currentTime', 'piggy', 'helloOrHi', 'exception', 'responseException', 'counter', 'redirectTrusted', 'redirectUntrusted'];
   }
 
 }
diff --git a/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php b/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php
index 08cabf578218faf02f29cfb39f9ff4fb002f2871..b7b197b968f7a2c2d26b73dd146b6ae32c07ba46 100644
--- a/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php
+++ b/core/modules/big_pipe/tests/src/Functional/BigPipeTest.php
@@ -219,6 +219,16 @@ public function testBigPipe(): void {
     $this->assertSession()->responseNotContains('</body>');
     // The exception is expected. Do not interpret it as a test failure.
     unlink($this->root . '/' . $this->siteDirectory . '/error.log');
+
+    // Tests the enforced redirect response exception handles redirecting to
+    // a trusted redirect.
+    $this->drupalGet(Url::fromRoute('big_pipe_test_trusted_redirect'));
+    $this->assertSession()->responseContains('application/vnd.drupal-ajax');
+    $this->assertSession()->responseContains('[{"command":"redirect","url":"\/big_pipe_test"}]');
+
+    // Test that it rejects an untrusted redirect.
+    $this->drupalGet(Url::fromRoute('big_pipe_test_untrusted_redirect'));
+    $this->assertSession()->responseContains('Redirects to external URLs are not allowed by default');
   }
 
   /**
diff --git a/core/modules/big_pipe/tests/src/Unit/Render/FiberPlaceholderTest.php b/core/modules/big_pipe/tests/src/Unit/Render/FiberPlaceholderTest.php
index 6e1ce3943ac081342f7bb598de3d5688a8f07f8c..644ed8eb051800b8ae3431301f4144ad8f661070 100644
--- a/core/modules/big_pipe/tests/src/Unit/Render/FiberPlaceholderTest.php
+++ b/core/modules/big_pipe/tests/src/Unit/Render/FiberPlaceholderTest.php
@@ -13,11 +13,13 @@
 use Drupal\Core\Render\PlaceholderGeneratorInterface;
 use Drupal\Core\Render\RenderCacheInterface;
 use Drupal\Core\Render\Renderer;
+use Drupal\Core\Routing\RequestContext;
 use Drupal\Core\Security\TrustedCallbackInterface;
 use Drupal\Core\Theme\ThemeManagerInterface;
 use Drupal\Core\Utility\CallableResolver;
 use Drupal\Tests\UnitTestCase;
 use Prophecy\Argument;
+use Psr\Log\LoggerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\HttpFoundation\Session\SessionInterface;
@@ -69,6 +71,8 @@ public function testLongPlaceholderFiberSuspendingLoop(): void {
       $this->createMock(EventDispatcherInterface::class),
       $this->prophesize(ConfigFactoryInterface::class)->reveal(),
       $this->prophesize(MessengerInterface::class)->reveal(),
+      $this->prophesize(RequestContext::class)->reveal(),
+      $this->prophesize(LoggerInterface::class)->reveal(),
     );
     $response = new BigPipeResponse(new HtmlResponse());
 
diff --git a/core/modules/big_pipe/tests/src/Unit/Render/ManyPlaceholderTest.php b/core/modules/big_pipe/tests/src/Unit/Render/ManyPlaceholderTest.php
index 7446def8ce056797b8f583f4ace2ee68da9d85b4..63fed38f5d7869dd127e6427cff54a24d0b07233 100644
--- a/core/modules/big_pipe/tests/src/Unit/Render/ManyPlaceholderTest.php
+++ b/core/modules/big_pipe/tests/src/Unit/Render/ManyPlaceholderTest.php
@@ -10,7 +10,9 @@
 use Drupal\Core\Messenger\MessengerInterface;
 use Drupal\Core\Render\HtmlResponse;
 use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Routing\RequestContext;
 use Drupal\Tests\UnitTestCase;
+use Psr\Log\LoggerInterface;
 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\HttpFoundation\Session\SessionInterface;
@@ -36,7 +38,9 @@ public function testManyNoJsPlaceHolders(): void {
       $this->prophesize(HttpKernelInterface::class)->reveal(),
       $this->prophesize(EventDispatcherInterface::class)->reveal(),
       $this->prophesize(ConfigFactoryInterface::class)->reveal(),
-      $this->prophesize(MessengerInterface::class)->reveal()
+      $this->prophesize(MessengerInterface::class)->reveal(),
+      $this->prophesize(RequestContext::class)->reveal(),
+      $this->prophesize(LoggerInterface::class)->reveal(),
     );
     $response = new BigPipeResponse(new HtmlResponse());