diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-94351-ExtextbaseStopActionException.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-94351-ExtextbaseStopActionException.rst
new file mode 100644
index 0000000000000000000000000000000000000000..8f918249676e0bd7742ccf5ad96c100977cf4d57
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-94351-ExtextbaseStopActionException.rst
@@ -0,0 +1,60 @@
+.. include:: ../../Includes.txt
+
+=====================================================
+Deprecation: #94351 - ext:extbase StopActionException
+=====================================================
+
+See :issue:`94351`
+
+Description
+===========
+
+To further prepare towards clean PSR-7 Request / Response handling in
+extbase, the extbase internal :php:`TYPO3\CMS\Extbase\Mvc\Exception\StopActionException`
+has been deprecated.
+
+
+Impact
+======
+
+No deprecation is logged, but the :php:`StopActionException` will be
+removed in v12 as breaking change. Extension developers with extbase
+based controllers can prepare in v11 towards this.
+
+
+Affected Installations
+======================
+
+Extensions with extbase controllers that throw :php:`StopActionException` or
+use methods :php:`redirect` or :php:`redirectToUri` from extbase :php:`ActionController`
+are affected.
+
+
+Migration
+=========
+
+As a goal, extbase actions will *always* return a :php:`ResponseInterface`
+in v12. v11 prepares towards this, but still throws the :php:`StopActionException`
+in :php:`redirectToUri`. Developers should prepare towards this, however.
+
+Example before:
+
+.. code-block:: php
+
+   public function fooAction()
+   {
+      $this->redirect('otherAction');
+   }
+
+Example compatible with v10, v11 and v12 - IDE's and static code analyzers
+may complain in v10 and v11, though:
+
+.. code-block:: php
+
+   public function fooAction(): ResponseInterface
+   {
+      // A return is added!
+      return $this->redirect('otherAction');
+   }
+
+.. index:: PHP-API, NotScanned, ext:extbase
diff --git a/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php b/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php
index 9a597cefd05a9550407326cf75ab27e721653fcb..46cfa6e8b75c7dc3ae4bb9b6f285fcc22d7b49ca 100644
--- a/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php
+++ b/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php
@@ -20,6 +20,7 @@ use Psr\Http\Message\ResponseFactoryInterface;
 use Psr\Http\Message\ResponseInterface;
 use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Http\PropagateResponseException;
+use TYPO3\CMS\Core\Http\RedirectResponse;
 use TYPO3\CMS\Core\Http\Response;
 use TYPO3\CMS\Core\Http\Stream;
 use TYPO3\CMS\Core\Messaging\AbstractMessage;
@@ -946,9 +947,6 @@ abstract class ActionController implements ControllerInterface
      *
      * Redirect will be sent to the client which then performs another request to the new URI.
      *
-     * NOTE: This method only supports web requests and will thrown an exception
-     * if used with other request types.
-     *
      * @param string $actionName Name of the action to forward to
      * @param string|null $controllerName Unqualified object name of the controller to forward to. If not specified, the current controller is used.
      * @param string|null $extensionName Name of the extension containing the controller to forward to. If not specified, the current extension is assumed.
@@ -956,9 +954,10 @@ abstract class ActionController implements ControllerInterface
      * @param int|null $pageUid Target page uid. If NULL, the current page uid is used
      * @param null $_ (optional) Unused
      * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other
-     * @throws StopActionException
+     * @throws StopActionException deprecated since TYPO3 11.0, method will RETURN a Core\Http\RedirectResponse instead of throwing in v12
+     * @todo: ': ResponseInterface' (without ?) in v12 as method return type together with redirectToUri() cleanup
      */
-    protected function redirect($actionName, $controllerName = null, $extensionName = null, array $arguments = null, $pageUid = null, $_ = null, $statusCode = 303)
+    protected function redirect($actionName, $controllerName = null, $extensionName = null, array $arguments = null, $pageUid = null, $_ = null, $statusCode = 303): void
     {
         if ($controllerName === null) {
             $controllerName = $this->request->getControllerName();
@@ -977,24 +976,17 @@ abstract class ActionController implements ControllerInterface
     /**
      * Redirects the web request to another uri.
      *
-     * NOTE: This method only supports web requests and will thrown an exception if used with other request types.
-     *
      * @param mixed $uri A string representation of a URI
      * @param null $_ (optional) Unused
      * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other"
-     * @throws StopActionException
+     * @throws StopActionException deprecated since TYPO3 11.0, will be removed in 12.0
+     * @todo: ': ResponseInterface' (without ?) in v12 as method return type together with redirectToUri() cleanup
      */
-    protected function redirectToUri($uri, $_ = null, $statusCode = 303)
+    protected function redirectToUri($uri, $_ = null, $statusCode = 303): void
     {
         $uri = $this->addBaseUriIfNecessary($uri);
-        $response = new HtmlResponse(
-            '',
-            $statusCode,
-            [
-                'Location' => (string)$uri
-            ]
-        );
-
+        $response = new RedirectResponse($uri, $statusCode);
+        // @deprecated since v11, will be removed in v12. RETURN the response instead. See Dispatcher class, too.
         throw new StopActionException('redirectToUri', 1476045828, null, $response);
     }
 
diff --git a/typo3/sysext/extbase/Classes/Mvc/Dispatcher.php b/typo3/sysext/extbase/Classes/Mvc/Dispatcher.php
index 094e2c2ab4e48b0e35b42519886b05bf029914d3..abc8c64679642ae700d84b68e318e514f0146304 100644
--- a/typo3/sysext/extbase/Classes/Mvc/Dispatcher.php
+++ b/typo3/sysext/extbase/Classes/Mvc/Dispatcher.php
@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Extbase\Mvc;
 use Psr\Container\ContainerInterface;
 use Psr\EventDispatcher\EventDispatcherInterface;
 use Psr\Http\Message\ResponseInterface;
+use TYPO3\CMS\Core\Http\RedirectResponse;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Extbase\Annotation\IgnoreValidation;
 use TYPO3\CMS\Extbase\Event\Mvc\AfterRequestDispatchedEvent;
@@ -90,8 +91,15 @@ class Dispatcher implements SingletonInterface
             try {
                 $response = $controller->processRequest($request);
                 if ($response instanceof ForwardResponse) {
+                    // The controller action returned an extbase internal Forward response:
+                    // Another action should be dispatched.
                     $request = static::buildRequestFromCurrentRequestAndForwardResponse($request, $response);
                 }
+                if ($response instanceof RedirectResponse) {
+                    // The controller action returned a core HTTP redirect response.
+                    // Dispatching ends here and response is sent to client.
+                    return $response;
+                }
             } catch (StopActionException $ignoredException) {
                 $response = $ignoredException->getResponse();
             }
diff --git a/typo3/sysext/extbase/Classes/Mvc/Exception/StopActionException.php b/typo3/sysext/extbase/Classes/Mvc/Exception/StopActionException.php
index abc7b75beccd7dcccb53e16e3ca70a836ab268f7..5d0ac115c3e2d92b128184b72188d39217909893 100644
--- a/typo3/sysext/extbase/Classes/Mvc/Exception/StopActionException.php
+++ b/typo3/sysext/extbase/Classes/Mvc/Exception/StopActionException.php
@@ -28,6 +28,10 @@ use TYPO3\CMS\Extbase\Mvc\Exception;
  * continues dispatching the request or returns control to the request handler.
  *
  * See the Action Controller's forward() and redirectToUri() methods for more information.
+ *
+ * @deprecated since v11, will be removed in v12. This action shouldn't be thrown anymore:
+ * Actions that extbase-internally forward to another action should RETURN Extbase\Http\ForwardResponse
+ * instead. Actions that initiate a client redirect should RETURN a Core\Http\RedirectResponse instead.
  */
 class StopActionException extends Exception
 {
@@ -38,6 +42,9 @@ class StopActionException extends Exception
 
     public function __construct($message = '', $code = 0, \Throwable $previous = null, ResponseInterface $response = null)
     {
+        // @deprecated since v11, will be removed in v12. Can not trigger_error() here since
+        // extbase ActionController still has to use this exception for b/w compatibility.
+        // See the usages of this exception when dropping in v12.
         $this->response = $response ?? new Response();
         parent::__construct($message, $code, $previous);
     }
diff --git a/typo3/sysext/extensionmanager/Classes/Controller/UploadExtensionFileController.php b/typo3/sysext/extensionmanager/Classes/Controller/UploadExtensionFileController.php
index cc2edf5ee2015fdc559eea67e05cd5136c6ad835..4371ab43b8253e6afd81634378d09c4c996123e9 100644
--- a/typo3/sysext/extensionmanager/Classes/Controller/UploadExtensionFileController.php
+++ b/typo3/sysext/extensionmanager/Classes/Controller/UploadExtensionFileController.php
@@ -98,7 +98,7 @@ class UploadExtensionFileController extends AbstractController
      * Extract an uploaded file and install the matching extension
      *
      * @param bool $overwrite Overwrite existing extension if TRUE
-     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
+     * @throws StopActionException @deprecated since v11, will be removed in v12
      */
     public function extractAction($overwrite = false)
     {
@@ -145,10 +145,12 @@ class UploadExtensionFileController extends AbstractController
                         FlashMessage::OK
                     );
                 } else {
+                    // @deprecated since v11, change to return $this->redirect()
                     $this->redirect('unresolvedDependencies', 'List', null, ['extensionKey' => $extensionKey]);
                 }
             }
         } catch (StopActionException $exception) {
+            // @deprecated since v11, will be removed in v12: redirect() will no longer throw in v12, drop this catch block
             throw $exception;
         } catch (InvalidFileException $exception) {
             $this->addFlashMessage($exception->getMessage(), '', FlashMessage::ERROR);
@@ -156,6 +158,7 @@ class UploadExtensionFileController extends AbstractController
             $this->removeExtensionAndRestoreFromBackup($fileName);
             $this->addFlashMessage($exception->getMessage(), '', FlashMessage::ERROR);
         }
+        // @deprecated since v11, change to return $this->redirect()
         $this->redirect('index', 'List', null, [
             self::TRIGGER_RefreshModuleMenu => true,
             self::TRIGGER_RefreshTopbar => true
diff --git a/typo3/sysext/form/Classes/Domain/Finishers/RedirectFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/RedirectFinisher.php
index 22eaf663c985540d1649447a70b36daf0d68aa29..f1c3b9186bac4e2d7b05520c0e7a6110db45ba26 100644
--- a/typo3/sysext/form/Classes/Domain/Finishers/RedirectFinisher.php
+++ b/typo3/sysext/form/Classes/Domain/Finishers/RedirectFinisher.php
@@ -17,10 +17,9 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Form\Domain\Finishers;
 
-use Psr\Http\Message\ResponseInterface;
-use TYPO3\CMS\Core\Http\Stream;
+use TYPO3\CMS\Core\Http\PropagateResponseException;
+use TYPO3\CMS\Core\Http\RedirectResponse;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
 use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
 
 /**
@@ -37,7 +36,6 @@ class RedirectFinisher extends AbstractFinisher
     protected $defaultOptions = [
         'pageUid' => 1,
         'additionalParameters' => '',
-        'delay' => 0,
         'statusCode' => 303,
     ];
 
@@ -46,11 +44,6 @@ class RedirectFinisher extends AbstractFinisher
      */
     protected $request;
 
-    /**
-     * @var ResponseInterface
-     */
-    protected $response;
-
     /**
      * @var \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder
      */
@@ -64,7 +57,6 @@ class RedirectFinisher extends AbstractFinisher
     {
         $formRuntime = $this->finisherContext->getFormRuntime();
         $this->request = $formRuntime->getRequest();
-        $this->response = $formRuntime->getResponse();
         $this->uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
         $this->uriBuilder->setRequest($this->request);
 
@@ -73,11 +65,10 @@ class RedirectFinisher extends AbstractFinisher
         $additionalParameters = $this->parseOption('additionalParameters');
         $additionalParameters = is_string($additionalParameters) ? $additionalParameters : '';
         $additionalParameters = '&' . ltrim($additionalParameters, '&');
-        $delay = (int)$this->parseOption('delay');
         $statusCode = (int)$this->parseOption('statusCode');
 
         $this->finisherContext->cancel();
-        $this->redirect($pageUid, $additionalParameters, $delay, $statusCode);
+        $this->redirect($pageUid, $additionalParameters, $statusCode);
     }
 
     /**
@@ -90,18 +81,17 @@ class RedirectFinisher extends AbstractFinisher
      *
      * @param int $pageUid Target page uid. If NULL, the current page uid is used
      * @param string $additionalParameters
-     * @param int $delay (optional) The delay in seconds. Default is no delay.
      * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other
      * @see forward()
      */
-    protected function redirect(int $pageUid = 1, string $additionalParameters = '', int $delay = 0, int $statusCode = 303)
+    protected function redirect(int $pageUid = 1, string $additionalParameters = '', int $statusCode = 303)
     {
         $typolinkConfiguration = [
             'parameter' => $pageUid,
             'additionalParams' => $additionalParameters,
         ];
         $redirectUri = $this->getTypoScriptFrontendController()->cObj->typoLink_URL($typolinkConfiguration);
-        $this->redirectToUri($redirectUri, $delay, $statusCode);
+        $this->redirectToUri($redirectUri, $statusCode);
     }
 
     /**
@@ -110,25 +100,18 @@ class RedirectFinisher extends AbstractFinisher
      * NOTE: This method only supports web requests and will thrown an exception if used with other request types.
      *
      * @param string $uri A string representation of a URI
-     * @param int $delay (optional) The delay in seconds. Default is no delay.
      * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other
-     * @throws StopActionException
+     * @throws PropagateResponseException
      */
-    protected function redirectToUri(string $uri, int $delay = 0, int $statusCode = 303)
+    protected function redirectToUri(string $uri, int $statusCode = 303)
     {
         $uri = $this->addBaseUriIfNecessary($uri);
-        $escapedUri = htmlentities($uri, ENT_QUOTES, 'utf-8');
-
-        $body = new Stream('php://temp', 'r+');
-        $body->write('<html><head><meta http-equiv="refresh" content="' . (int)$delay . ';url=' . $escapedUri . '"/></head></html>');
-        $body->rewind();
-
-        $this->response = $this->response
-            ->withBody($body)
-            ->withStatus($statusCode)
-            ->withHeader('Location', (string)$uri)
-        ;
-        throw new StopActionException('redirectToUri', 1477070964, null, $this->response);
+        $response = new RedirectResponse($uri, $statusCode);
+        // End processing and dispatching by throwing a PropagateResponseException with our response.
+        // @todo: Should be changed to *return* a response instead, but this requires the ContentObjectRender
+        // @todo: to deal with responses instead of strings, if the form is used in a fluid template rendered by the
+        // @todo: FluidTemplateContentObject and the extbase bootstrap isn't used.
+        throw new PropagateResponseException($response, 1477070964);
     }
 
     /**
diff --git a/typo3/sysext/form/Classes/ViewHelpers/RenderViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/RenderViewHelper.php
index 5e91edf626f75d855dd039369b7539a951882767..92dfded347adf27aa9e0005bfc71806107b82319 100644
--- a/typo3/sysext/form/Classes/ViewHelpers/RenderViewHelper.php
+++ b/typo3/sysext/form/Classes/ViewHelpers/RenderViewHelper.php
@@ -111,6 +111,13 @@ class RenderViewHelper extends AbstractViewHelper
         try {
             return $form->render();
         } catch (StopActionException $exception) {
+            // @deprecated since v11, will be removed in v12: StopActionException is deprecated, drop this catch block.
+            // RedirectFinisher for throws a PropagateResponseException instead which bubbles up into Middleware.
+            trigger_error(
+                'Throwing StopActionException is deprecated. If really needed, throw a (internal) PropagateResponseException'
+                . ' instead, for now. Note this is subject to change.',
+                E_USER_DEPRECATED
+            );
             return $exception->getResponse()->getBody()->getContents();
         }
     }
diff --git a/typo3/sysext/form/Tests/Unit/Domain/Finishers/RedirectFinisherTest.php b/typo3/sysext/form/Tests/Unit/Domain/Finishers/RedirectFinisherTest.php
index a9f71d926a0a5b9e5331f004c1da0f30196799f9..f7358a4efde1faeb0f2ff9d45b92bfc5f5f43b1e 100644
--- a/typo3/sysext/form/Tests/Unit/Domain/Finishers/RedirectFinisherTest.php
+++ b/typo3/sysext/form/Tests/Unit/Domain/Finishers/RedirectFinisherTest.php
@@ -18,9 +18,9 @@ declare(strict_types=1);
 namespace TYPO3\CMS\Form\Tests\Unit\Domain\Finishers;
 
 use Prophecy\Argument;
+use TYPO3\CMS\Core\Http\PropagateResponseException;
 use TYPO3\CMS\Core\Http\Response;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
 use TYPO3\CMS\Extbase\Mvc\Request;
 use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
 use TYPO3\CMS\Extbase\Object\Exception;
@@ -84,7 +84,7 @@ class RedirectFinisherTest extends UnitTestCase
         try {
             $redirectFinisherMock->execute($finisherContextProphecy->reveal());
             self::fail('RedirectFinisher did not throw expected exception.');
-        } /** @noinspection PhpRedundantCatchClauseInspection */ catch (StopActionException $e) {
+        } /** @noinspection PhpRedundantCatchClauseInspection */ catch (PropagateResponseException $e) {
             $response = $e->getResponse();
             self::assertSame($uriPrefix . $expectedPage, $response->getHeader('Location')[0]);
         }