diff --git a/typo3/sysext/core/Classes/Error/PageErrorHandler/PageContentErrorHandler.php b/typo3/sysext/core/Classes/Error/PageErrorHandler/PageContentErrorHandler.php
index 1562280f02f6782fd33283bcd7e0d00570cd6315..f40590c9079d9bbf296f4099de4fa3b75c535eac 100644
--- a/typo3/sysext/core/Classes/Error/PageErrorHandler/PageContentErrorHandler.php
+++ b/typo3/sysext/core/Classes/Error/PageErrorHandler/PageContentErrorHandler.php
@@ -19,10 +19,11 @@ namespace TYPO3\CMS\Core\Error\PageErrorHandler;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Cache\CacheManager;
+use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException;
 use TYPO3\CMS\Core\Exception\SiteNotFoundException;
 use TYPO3\CMS\Core\Http\HtmlResponse;
 use TYPO3\CMS\Core\Http\RequestFactory;
-use TYPO3\CMS\Core\Http\Response;
 use TYPO3\CMS\Core\LinkHandling\LinkService;
 use TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException;
 use TYPO3\CMS\Core\Site\Entity\Site;
@@ -47,6 +48,11 @@ class PageContentErrorHandler implements PageErrorHandlerInterface
      */
     protected $errorHandlerConfiguration;
 
+    /**
+     * @var int
+     */
+    protected $pageUid = 0;
+
     /**
      * PageContentErrorHandler constructor.
      * @param int $statusCode
@@ -68,25 +74,48 @@ class PageContentErrorHandler implements PageErrorHandlerInterface
      * @param array $reasons
      * @return ResponseInterface
      * @throws \RuntimeException
+     * @throws NoSuchCacheException
      */
     public function handlePageError(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
     {
         try {
             $resolvedUrl = $this->resolveUrl($request, $this->errorHandlerConfiguration['errorContentSource']);
-            $content = null;
-            if ($resolvedUrl !== (string)$request->getUri()) {
+
+            $cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('pages');
+            $cacheIdentifier = 'errorPage_' . md5($resolvedUrl);
+            $cacheContent = $cache->get($cacheIdentifier);
+
+            if (!$cacheContent && $resolvedUrl !== (string)$request->getUri()) {
                 try {
-                    $subResponse = GeneralUtility::makeInstance(RequestFactory::class)->request($resolvedUrl, 'GET');
+                    $subResponse = GeneralUtility::makeInstance(RequestFactory::class)
+                        ->request($resolvedUrl, 'GET', $this->getSubRequestOptions());
                 } catch (\Exception $e) {
                     throw new \RuntimeException('Error handler could not fetch error page "' . $resolvedUrl . '", reason: ' . $e->getMessage(), 1544172838);
                 }
                 if ($subResponse->getStatusCode() >= 300) {
                     throw new \RuntimeException('Error handler could not fetch error page "' . $resolvedUrl . '", status code: ' . $subResponse->getStatusCode(), 1544172839);
                 }
-                // create new response object and re-use only the body and the content-type of the sub-request
-                return new Response($subResponse->getBody(), $this->statusCode, [
-                    'Content-Type' => $subResponse->getHeader('Content-Type')
-                ]);
+
+                $body = $subResponse->getBody()->getContents();
+                $contentType = $subResponse->getHeader('Content-Type');
+
+                // Cache body and content-type if sub-response returned a HTTP status 200
+                if ($subResponse->getStatusCode() === 200) {
+                    $cacheTags = ['errorPage'];
+                    if ($this->pageUid > 0) {
+                        // Cache Tag "pageId_" ensures, cache is purged when content of 404 page changes
+                        $cacheTags[] = 'pageId_' . $this->pageUid;
+                    }
+                    $cacheContent = [
+                        'body' => $body,
+                        'headers' => ['Content-Type' => $contentType],
+                    ];
+                    $cache->set($cacheIdentifier, $cacheContent, $cacheTags);
+                }
+            }
+            if ($cacheContent && $cacheContent['body'] && $cacheContent['headers']) {
+                // We use a HtmlResponse here, since no Stream is available for cached response content
+                return new HtmlResponse($cacheContent['body'], $this->statusCode, $cacheContent['headers']);
             }
             $content = 'The error page could not be resolved, as the error page itself is not accessible';
         } catch (InvalidRouteArgumentsException | SiteNotFoundException $e) {
@@ -95,6 +124,22 @@ class PageContentErrorHandler implements PageErrorHandlerInterface
         return new HtmlResponse($content, $this->statusCode);
     }
 
+    /**
+     * Returns request options for the subrequest and ensures, that a reasoneable timeout is present
+     *
+     * @return array|int[]
+     */
+    protected function getSubRequestOptions(): array
+    {
+        $options = [];
+        if ((int)$GLOBALS['TYPO3_CONF_VARS']['HTTP']['timeout'] === 0) {
+            $options = [
+                'timeout' => 30
+            ];
+        }
+        return $options;
+    }
+
     /**
      * Resolve the URL (currently only page and external URL are supported)
      *
@@ -115,8 +160,10 @@ class PageContentErrorHandler implements PageErrorHandlerInterface
             return $urlParams['url'];
         }
 
+        $this->pageUid = (int)$urlParams['pageuid'];
+
         // Get the site related to the configured error page
-        $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId((int)$urlParams['pageuid']);
+        $site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId($this->pageUid);
         // Fall back to current request for the site
         if (!$site instanceof Site) {
             $site = $request->getAttribute('site', null);
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Important-88824-AddCacheForErrorPageHandling.rst b/typo3/sysext/core/Documentation/Changelog/master/Important-88824-AddCacheForErrorPageHandling.rst
new file mode 100644
index 0000000000000000000000000000000000000000..2688a87e25d178b6a0ef97381de79bd5aa90bbbe
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Important-88824-AddCacheForErrorPageHandling.rst
@@ -0,0 +1,24 @@
+.. include:: ../../Includes.txt
+
+=====================================================
+Important: #88824 - Add cache for error page handling
+=====================================================
+
+See :issue:`88824`
+
+Description
+===========
+
+In order to prevent possible DoS attacks when the page-based error handler
+is used, the content of the 404 error page is now cached in the TYPO3
+page cache. Any dynamic content on the error page (e.g. content created
+by TypoScript or uncached plugins) will therefore also be cached.
+
+If the 404 error page contains dynamic content, TYPO3 administrators must
+ensure that no sensitive data (e.g. username of logged in frontend user)
+will be shown on the error page.
+
+If dynamic content is required on the 404 error page, it is recommended
+to implement a custom PHP based error handler.
+
+.. index:: Backend, ext:backend