diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst
new file mode 100644
index 0000000000000000000000000000000000000000..8e4442562f4dab9055ce94c5d1dbdadf0e0cfb31
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst
@@ -0,0 +1,65 @@
+.. include:: ../../Includes.txt
+
+==================================================================
+Deprecation: #84965 - Various TypoScriptFrontendController methods
+==================================================================
+
+See :issue:`84965`
+
+Description
+===========
+
+A lot of functionality from TypoScriptFrontendController (a.k.a. `TSFE`) has been migrated
+into new PSR-15 middlewares, which are flexible modules to modify a HTTP request workflow.
+
+Most of the functionality which is now in a PSR-15-based middleware is related to setting up various
+permission and GET/POST variable resolving.
+
+The following methods within :php:`TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController` have been marked
+as deprecated:
+
+* connectToDB()
+* checkAlternativeIdMethods()
+* initializeBackendUser()
+* handleDataSubmission()
+* setCSS()
+* convPOSTCharset()
+
+All hooks previously located within these methods still work as expected, as they are now called within
+a PSR-15 middleware.
+
+Additionally, there are some methods within TSFE which have been marked as "internal" for a long time,
+but had the PHP visibility "public" from a legacy code base. These methods, which are internal for TYPO3 Core
+purposes, now have the visibility "protected".
+
+- getPageAndRootline()
+- checkRootlineForIncludeSection()
+- setSysPageWhereClause()
+- checkAndSetAlias()
+- getHash()
+- getLockHash()
+- setUrlIdToken()
+
+
+Impact
+======
+
+Calling any of the deprecated methods above will trigger a PHP deprecation message.
+
+
+Affected Installations
+======================
+
+Any TYPO3 installation with a custom extension setting up or mimicking a custom frontend request by
+calling TypoScriptFrontendController methods directly.
+
+
+Migration
+=========
+
+Extensions that bootstrap their own frontend should ensure that the respective Middlewares are run,
+e.g. via custom stacks or just by setting up the "frontend" middleware stack.
+
+Additionally, extensions can create custom middlewares to modify a HTTP request or response as well.
+
+.. index:: Frontend, PHP-API, FullyScanned, ext:frontend
\ No newline at end of file
diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
index 3bb1bdcad244d2b555e5779596e3029b72c9aebf..ae43cf96218240f94d7523dd4c4d708cd245f97c 100644
--- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
+++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
@@ -840,9 +840,11 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      *
      * @throws \RuntimeException
      * @throws ServiceUnavailableException
+     * @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0.
      */
     public function connectToDB()
     {
+        trigger_error('The method "' . __METHOD__ . '" will be removed in TYPO3 v10.0, as the database connection is checked in the TypoScriptFrontendInitialization middleware.', E_USER_DEPRECATED);
         try {
             $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
             $connection->connect();
@@ -976,9 +978,12 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      * Two options:
      * 1) Use PATH_INFO (also Apache) to extract id and type from that var. Does not require any special modules compiled with apache. (less typical)
      * 2) Using hook which enables features like those provided from "realurl" extension (AKA "Speaking URLs")
+     *
+     * @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0.
      */
     public function checkAlternativeIdMethods()
     {
+        trigger_error('This method "' . __METHOD__ . '" is removed, extensions should use a Frontend PSR-15-based middleware to hook into the frontend process. There is no need to call this method directly.', E_USER_DEPRECATED);
         $this->siteScript = GeneralUtility::getIndpEnv('TYPO3_SITE_SCRIPT');
         // Call post processing function for custom URL methods.
         $_params = ['pObj' => &$this];
@@ -1015,9 +1020,11 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      * Creates the backend user object and returns it.
      *
      * @return FrontendBackendUserAuthentication the backend user object
+     * @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0.
      */
     public function initializeBackendUser()
     {
+        trigger_error('The method "' . __METHOD__ . '" is deprecated, and will be removed in TYPO3 v10. Extensions should ensure that the BackendAuthenticator middleware is run to load a backend user.', E_USER_DEPRECATED);
         // PRE BE_USER HOOK
         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'] ?? [] as $_funcRef) {
             $_params = [];
@@ -1382,9 +1389,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      *
      * @throws ServiceUnavailableException
      * @throws PageNotFoundException
-     * @access private
      */
-    public function getPageAndRootline()
+    protected function getPageAndRootline()
     {
         $this->resolveTranslatedPageId();
         if (empty($this->page)) {
@@ -1603,9 +1609,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      * @todo Invert boolean return value. Return true if visible.
      *
      * @return bool
-     * @access private
      */
-    public function checkRootlineForIncludeSection(): bool
+    protected function checkRootlineForIncludeSection(): bool
     {
         $c = count($this->rootLine);
         $removeTheRestFlag = false;
@@ -1816,10 +1821,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface
 
     /**
      * Sets sys_page where-clause
-     *
-     * @access private
      */
-    public function setSysPageWhereClause()
+    protected function setSysPageWhereClause()
     {
         $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
             ->getConnectionForTable('pages')
@@ -2063,10 +2066,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface
     /**
      * Fetches the integer page id for a page alias.
      * Looks if ->id is not an integer and if so it will search for a page alias and if found the page uid of that page is stored in $this->id
-     *
-     * @access private
      */
-    public function checkAndSetAlias()
+    protected function checkAndSetAlias()
     {
         if ($this->id && !MathUtility::canBeInterpretedAsInteger($this->id)) {
             $aid = $this->sys_page->getPageIdFromAlias($this->id);
@@ -2350,10 +2351,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      * Used to get and later store the cached data.
      *
      * @return string MD5 hash of serialized hash base from createHashBase()
-     * @access private
      * @see getFromCache(), getLockHash()
      */
-    public function getHash()
+    protected function getHash()
     {
         return md5($this->createHashBase(false));
     }
@@ -2363,10 +2363,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      * This hash is unique to the above hash, except that it doesn't contain the template information in $this->all.
      *
      * @return string MD5 hash
-     * @access private
      * @see getFromCache(), getHash()
      */
-    public function getLockHash()
+    protected function getLockHash()
     {
         $lockHash = $this->createHashBase(true);
         return md5($lockHash);
@@ -2745,9 +2744,11 @@ class TypoScriptFrontendController implements LoggerAwareInterface
     /**
      * Handle data submission
      * This is done at this point, because we need the config values
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
      */
     public function handleDataSubmission()
     {
+        trigger_error('The method "' . __METHOD__ . '" is deprecated since TYPO3 v9, and will be removed in TYPO3 v10. Implement the hooks directly, as they are still executed within TYPO3 via a PSR-15 middleware.', E_USER_DEPRECATED);
         // Hook for processing data submission to extensions
         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'] ?? [] as $className) {
             $_procObj = GeneralUtility::makeInstance($className);
@@ -2815,10 +2816,8 @@ class TypoScriptFrontendController implements LoggerAwareInterface
     /**
      * Sets the URL_ID_TOKEN in the internal var, $this->getMethodUrlIdToken
      * This feature allows sessions to use a GET-parameter instead of a cookie.
-     *
-     * @access private
      */
-    public function setUrlIdToken()
+    protected function setUrlIdToken()
     {
         if ($this->config['config']['ftu']) {
             $this->getMethodUrlIdToken = $GLOBALS['TYPO3_CONF_VARS']['FE']['get_url_id_token'];
@@ -4125,9 +4124,12 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      * @param string $key Is the key in the array, for num-key let the value be empty
      * @param string $content Is the content if you want any
      * @see setJS()
+     *
+     * @deprecated since TYPO3 v9.3, will be removed in TYPO3 v10.0
      */
     public function setCSS($key, $content)
     {
+        trigger_error('The method "' . __METHOD__ . '" will be removed in TYPO3 v10, use PageRenderer instead to add CSS.', E_USER_DEPRECATED);
         if ($key) {
             $this->additionalCSS[$key] = $content;
         }
@@ -4374,9 +4376,11 @@ class TypoScriptFrontendController implements LoggerAwareInterface
 
     /**
      * Converts the $_POST array from metaCharset (page HTML charset from input form) to utf-8 (internal processing) IF the two charsets are different.
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
      */
     public function convPOSTCharset()
     {
+        trigger_error('The method "' . __METHOD__ . '" is deprecated since TYPO3 v9, and will be removed in TYPO3 v10. A PSR-15 middleware is now taking care of the conversion. It seems you called this method from your own bootstrap code - ensure that the PrepareTypoScriptFrontendRendering middleware is called and you can remove the method call.', E_USER_DEPRECATED);
         if ($this->metaCharset !== 'utf-8' && is_array($_POST) && !empty($_POST)) {
             $this->convertCharsetRecursivelyToUtf8($_POST, $this->metaCharset);
             $GLOBALS['HTTP_POST_VARS'] = $_POST;
@@ -4388,6 +4392,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      *
      * @param mixed $data given by reference (string/array usually)
      * @param string $fromCharset convert FROM this charset
+     * @deprecated since TYPO3 v9, will be removed when convPOSTCharset() is removed as well in TYPO3 v10.
      */
     protected function convertCharsetRecursivelyToUtf8(&$data, string $fromCharset)
     {
diff --git a/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php b/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php
index 45129c6609cbb1b3adf06c5112202fe094543165..d0e0ded10dfbf9f6b62be807bf7e7c1bdfe20d9d 100644
--- a/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php
+++ b/typo3/sysext/frontend/Classes/Middleware/PrepareTypoScriptFrontendRendering.php
@@ -75,11 +75,40 @@ class PrepareTypoScriptFrontendRendering implements MiddlewareInterface
         $this->timeTracker->pull();
 
         // Convert POST data to utf-8 for internal processing if metaCharset is different
-        $this->controller->convPOSTCharset();
+        if ($this->controller->metaCharset !== 'utf-8' && is_array($_POST) && !empty($_POST)) {
+            $this->convertCharsetRecursivelyToUtf8($_POST, $this->controller->metaCharset);
+            $GLOBALS['HTTP_POST_VARS'] = $_POST;
+            $parsedBody = $request->getParsedBody();
+            $this->convertCharsetRecursivelyToUtf8($parsedBody, $this->controller->metaCharset);
+            $request = $request->withParsedBody($parsedBody);
+            $GLOBALS['TYPO3_REQUEST'] = $request;
+        }
 
         $this->controller->initializeRedirectUrlHandlers();
-        $this->controller->handleDataSubmission();
+
+        // Hook for processing data submission to extensions
+        // This is done at this point, because we need the config values
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'] ?? [] as $className) {
+            GeneralUtility::makeInstance($className)->checkDataSubmission($this->controller);
+        }
 
         return $handler->handle($request);
     }
+
+    /**
+     * Small helper function to convert charsets for arrays to UTF-8
+     *
+     * @param mixed $data given by reference (string/array usually)
+     * @param string $fromCharset convert FROM this charset
+     */
+    protected function convertCharsetRecursivelyToUtf8(&$data, string $fromCharset)
+    {
+        foreach ($data as $key => $value) {
+            if (is_array($data[$key])) {
+                $this->convertCharsetRecursivelyToUtf8($data[$key], $fromCharset);
+            } elseif (is_string($data[$key])) {
+                $data[$key] = mb_convert_encoding($data[$key], 'utf-8', $fromCharset);
+            }
+        }
+    }
 }
diff --git a/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php b/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php
index 5e2db74da160d0c58d6e62f951cc687440e8a3fd..0838cecb9c8d57282dad384daa1707173694c982 100644
--- a/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php
+++ b/typo3/sysext/frontend/Classes/Middleware/TypoScriptFrontendInitialization.php
@@ -15,11 +15,17 @@ namespace TYPO3\CMS\Frontend\Middleware;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Doctrine\DBAL\Exception\ConnectionException;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
+use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Error\Http\ServiceUnavailableException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\Controller\ErrorController;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
 /**
@@ -31,10 +37,13 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
  *
  * @internal
  */
-class TypoScriptFrontendInitialization implements MiddlewareInterface
+class TypoScriptFrontendInitialization implements MiddlewareInterface, LoggerAwareInterface
 {
+    use LoggerAwareTrait;
+
     /**
-     * Creates an instance of TSFE and sets it as a global variable
+     * Creates an instance of TSFE and sets it as a global variable,
+     * also pings the database in order ensure a valid database connection.
      *
      * @param ServerRequestInterface $request
      * @param RequestHandlerInterface $handler
@@ -52,7 +61,27 @@ class TypoScriptFrontendInitialization implements MiddlewareInterface
             null,
             GeneralUtility::_GP('MP')
         );
-        $GLOBALS['TSFE']->connectToDB();
+
+        // Set up the database connection and see if the connection can be established
+        try {
+            $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
+            $connection->connect();
+        } catch (ConnectionException $exception) {
+            // Cannot connect to current database
+            $message = 'Cannot connect to the configured database "' . $connection->getDatabase() . '"';
+            $this->logger->emergency($message, ['exception' => $exception]);
+            try {
+                return GeneralUtility::makeInstance(ErrorController::class)->unavailableAction($request, $message);
+            } catch (ServiceUnavailableException $e) {
+                throw new ServiceUnavailableException($message, 1526013723);
+            }
+        }
+        // Call post processing function for DB connection:
+        $_params = ['pObj' => &$GLOBALS['TSFE']];
+        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'] ?? [] as $_funcRef) {
+            GeneralUtility::callUserFunction($_funcRef, $_params, $GLOBALS['TSFE']);
+        }
+
         return $handler->handle($request);
     }
 }
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
index 95eb70c325c844ca0feef5ac966abb7472abf639..e94bd50f86a770414ca0b057969525d75ae896f5 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
@@ -2130,4 +2130,46 @@ return [
             'Deprecation-84725-SysDomainResolvingMovedIntoMiddleware.rst',
         ],
     ],
+    'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->connectToDB' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->checkAlternativeIdMethods' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->initializeBackendUser' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->handleDataSubmission' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->setCSS' => [
+        'numberOfMandatoryArguments' => 2,
+        'maximumNumberOfArguments' => 2,
+        'restFiles' => [
+            'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->convPOSTCharset' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84965-VariousTypoScriptFrontendControllerMethods.rst',
+        ],
+    ],
 ];
diff --git a/typo3/sysext/redirects/Classes/Service/RedirectService.php b/typo3/sysext/redirects/Classes/Service/RedirectService.php
index 5f0f8b363294c374b4ed0e536710fd0295ef0df6..9593c42f13a724743e8327bf4398e0ade2e490a0 100644
--- a/typo3/sysext/redirects/Classes/Service/RedirectService.php
+++ b/typo3/sysext/redirects/Classes/Service/RedirectService.php
@@ -246,8 +246,6 @@ class RedirectService implements LoggerAwareInterface
             GeneralUtility::_GP('id'),
             GeneralUtility::_GP('type')
         );
-        $GLOBALS['TSFE']->initFEuser();
-        $GLOBALS['TSFE']->initializeBackendUser();
         $GLOBALS['TSFE']->fetch_the_id();
         $GLOBALS['TSFE']->initTemplate();
         $GLOBALS['TSFE']->getConfigArray();