diff --git a/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97862-HooksRelatedToGeneratingPageContentRemoved.rst b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97862-HooksRelatedToGeneratingPageContentRemoved.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bfa1601e8c9ec1a689ac6912b3bc1f8bdb09ce7d
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97862-HooksRelatedToGeneratingPageContentRemoved.rst
@@ -0,0 +1,77 @@
+.. include:: /Includes.rst.txt
+
+.. _breaking-97862-1657195630:
+
+===================================================================
+Breaking: #97862 - Hooks related to generating page content removed
+===================================================================
+
+See :issue:`97862`
+
+Description
+===========
+
+The existing TYPO3 hooks in the process of generating a TYPO3 Frontend page
+
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-cached']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['usePageCache']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['insertPageIncache']`
+
+have been removed. These hooks have been used to execute custom PHP code after
+a page is generated in the TYPO3 frontend and ready to be stored in cache.
+
+Due to the removal of the hooks and the introduction of the new PSR-14 Events
+has the method signature of :php:`TypoScriptFrontendController->generatePage_postProcessing()`
+been changed. The method now requires a :php:`ServerRequestInterface` as first
+argument.
+
+Impact
+======
+
+Extension code that hooks into these places will not be executed anymore in
+TYPO3 v12+.
+
+Extension code calling :php:`TypoScriptFrontendController->generatePage_postProcessing()`
+without providing a :php:`ServerRequestInterface` as first argument
+will trigger a PHP `ArgumentCountError`.
+
+Affected installations
+======================
+
+TYPO3 installations with custom extensions using these hooks such as static file
+generation or modifying the page content cache, which is highly likely in
+third-party extensions. The extension scanner will detect usages as
+strong match.
+
+Extensions, manually calling :php:`TypoScriptFrontendController->generatePage_postProcessing()`
+without providing a :php:`ServerRequestInterface` as first argument. The
+extension scanner will detect usages as weak match.
+
+Migration
+=========
+
+Use one of the two newly introduced :doc:`PSR-14 events <../12.0/Feature-97862-NewPSR-14EventsForManipulatingFrontendPageGenerationAndCacheBehaviour>`:
+
+* :php:`TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent`
+* :php:`TYPO3\CMS\Frontend\Event\AfterCachedPageIsPersistedEvent`
+
+Extensions using the hooks can be made compatible with TYPO3 v11 and TYPO3 v12
+by registering a PSR-14-based Event Listener while keeping the legacy hook
+in place.
+
+The :php:`AfterCacheableContentIsGeneratedEvent` acts as a replacement for
+
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-cached']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['usePageCache']`
+
+whereas the :php:`AfterCachedPageIsPersistedEvent` is the replacement for
+
+:php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['insertPageIncache']`.
+
+Provide a :php:`ServerRequestInterface` as first argument when calling
+:php:`TypoScriptFrontendController->generatePage_postProcessing()` in custom
+extension code.
+
+.. index:: Frontend, PHP-API, FullyScanned, ext:frontend
diff --git a/typo3/sysext/core/Documentation/Changelog/12.0/Feature-97862-NewPSR-14EventsForManipulatingFrontendPageGenerationAndCacheBehaviour.rst b/typo3/sysext/core/Documentation/Changelog/12.0/Feature-97862-NewPSR-14EventsForManipulatingFrontendPageGenerationAndCacheBehaviour.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b53aee9af459b81923bf4957841ed5fa9312e09a
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.0/Feature-97862-NewPSR-14EventsForManipulatingFrontendPageGenerationAndCacheBehaviour.rst
@@ -0,0 +1,78 @@
+.. include:: /Includes.rst.txt
+
+.. _feature-97862-1657195761:
+
+=================================================================================================
+Feature: #97862 - New PSR-14 events for manipulating frontend page generation and cache behaviour
+=================================================================================================
+
+See :issue:`97862`
+
+Description
+===========
+
+Two new PSR-14 Events have been added:
+
+* :php:`TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent`
+* :php:`TYPO3\CMS\Frontend\Event\AfterCachedPageIsPersistedEvent`
+
+They are added in favor of the :doc:`removed hooks <../12.0/Breaking-97862-HooksRelatedToGeneratingPageContentRemoved>`:
+
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-cached']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['usePageCache']`
+* :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['insertPageIncache']`
+
+Both events are called when the content of a page has been
+generated in the TYPO3 Frontend.
+
+Example
+=======
+
+Registration of the `AfterCacheableContentIsGeneratedEvent` in your extensions' :file:`Services.yaml`:
+
+.. code-block:: yaml
+
+  MyVendor\MyPackage\Backend\MyEventListener:
+    tags:
+      - name: event.listener
+        identifier: 'my-package/content-modifier'
+
+The corresponding event listener class:
+
+.. code-block:: php
+
+    use TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent;
+
+    class MyEventListener {
+
+        public function __invoke(AfterCacheableContentIsGeneratedEvent $event): void
+        {
+            // Only do this when caching is enabled
+            if (!$event->isCachingEnabled()) {
+                return;
+            }
+            $event->getController()->content = str_replace('foo', 'bar', $event->getController()->content);
+        }
+    }
+
+Impact
+======
+
+The Event `AfterCacheableContentIsGeneratedEvent` can be used
+to decide if a page should be stored in cache and is executed right after
+all cacheable content is generated. It can also be used to manipulate
+the content before it is stored in TYPO3's page cache. The Event is used
+in indexed search to index cacheable content.
+
+The :php:`AfterCacheableContentIsGeneratedEvent` contains the
+information if a generated page is able to store in cache via the
+:php:`$event->isCachingEnabled()` method. This can be used to
+differentiate between the previous hooks `contentPostProc-cached` and
+`contentPostProc-all` (do something regardless if caching is enabled or not).
+
+The :php:`AfterCachedPageIsPersistedEvent` is commonly used to
+generate a static file cache. This event is only called if the
+page was actually stored in TYPO3's page cache.
+
+.. index:: Frontend, PHP-API, ext:frontend
diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
index 6f5219a26ed9584438cab5ca92fc5917f4b4267e..9c38f617b379cd938b29c2f3b82ae8d93b24abce 100644
--- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
+++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
@@ -68,6 +68,8 @@ use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
 use TYPO3\CMS\Frontend\Cache\CacheLifetimeCalculator;
 use TYPO3\CMS\Frontend\Configuration\TypoScript\ConditionMatching\ConditionMatcher;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent;
+use TYPO3\CMS\Frontend\Event\AfterCachedPageIsPersistedEvent;
 use TYPO3\CMS\Frontend\Event\AfterPageAndLanguageIsResolvedEvent;
 use TYPO3\CMS\Frontend\Event\AfterPageWithRootLineIsResolvedEvent;
 use TYPO3\CMS\Frontend\Event\BeforePageIsResolvedEvent;
@@ -1629,42 +1631,16 @@ class TypoScriptFrontendController implements LoggerAwareInterface
     }
 
     /**
-     * Set cache content to $this->content
-     */
-    protected function realPageCacheContent()
-    {
-        // seconds until a cached page is too old
-        $cacheTimeout = $this->get_cache_timeout();
-        $timeOutTime = $GLOBALS['EXEC_TIME'] + $cacheTimeout;
-        $usePageCache = true;
-        // Hook for deciding whether page cache should be written to the cache backend or not
-        // NOTE: as hooks are called in a loop, the last hook will have the final word (however each
-        // hook receives the current status of the $usePageCache flag)
-        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['usePageCache'] ?? [] as $className) {
-            $usePageCache = GeneralUtility::makeInstance($className)->usePageCache($this, $usePageCache);
-        }
-        // Write the page to cache, if necessary
-        if ($usePageCache) {
-            $this->setPageCacheContent($this->content, $this->config, $timeOutTime);
-        }
-        // Hook for cache post processing (eg. writing static files!)
-        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['insertPageIncache'] ?? [] as $className) {
-            GeneralUtility::makeInstance($className)->insertPageIncache($this, $timeOutTime);
-        }
-    }
-
-    /**
-     * Sets cache content; Inserts the content string into the cache_pages cache.
+     * Sets cache content; Inserts the content string into the pages cache.
      *
      * @param string $content The content to store in the HTML field of the cache table
-     * @param mixed $data The additional cache_data array, fx. $this->config
+     * @param array $data The additional cache_data array, fx. $this->config
      * @param int $expirationTstamp Expiration timestamp
-     * @see realPageCacheContent()
+     * @see populatePageDataFromCache()
      */
-    protected function setPageCacheContent($content, $data, $expirationTstamp)
+    protected function setPageCacheContent(string $content, array $data, int $expirationTstamp): array
     {
         $cacheData = [
-            'identifier' => $this->newHash,
             'page_id' => $this->id,
             'content' => $content,
             'cache_data' => $data,
@@ -1672,7 +1648,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface
             'tstamp' => $GLOBALS['EXEC_TIME'],
         ];
         $this->cacheExpires = $expirationTstamp;
-        $this->pageCacheTags[] = 'pageId_' . $cacheData['page_id'];
+        $this->pageCacheTags[] = 'pageId_' . $this->id;
         // Respect the page cache when content of pid is shown
         if ($this->id !== $this->contentPid) {
             $this->pageCacheTags[] = 'pageId_' . $this->contentPid;
@@ -1681,9 +1657,11 @@ class TypoScriptFrontendController implements LoggerAwareInterface
             $tags = GeneralUtility::trimExplode(',', $this->page['cache_tags'], true);
             $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
         }
+        $this->pageCacheTags = array_unique($this->pageCacheTags);
         // Add the cache themselves as well, because they are fetched by getPageCacheTags()
         $cacheData['cacheTags'] = $this->pageCacheTags;
         $this->pageCache->set($this->newHash, $cacheData, $this->pageCacheTags, $expirationTstamp - $GLOBALS['EXEC_TIME']);
+        return $cacheData;
     }
 
     /**
@@ -1878,30 +1856,30 @@ class TypoScriptFrontendController implements LoggerAwareInterface
      *
      * This includes caching the page, indexing the page (if configured) and setting sysLastChanged
      */
-    public function generatePage_postProcessing()
+    public function generatePage_postProcessing(ServerRequestInterface $request)
     {
         $this->setAbsRefPrefix();
-        // This is to ensure, that the page is NOT cached if the no_cache parameter was set before the page was generated. This is a safety precaution, as it could have been unset by some script.
+        // This is to ensure, that the page is NOT cached if the no_cache parameter was set before the page was generated.
+        // This is a safety precaution, as it could have been unset by some script.
         if ($this->no_cacheBeforePageGen) {
             $this->set_no_cache('no_cache has been set before the page was generated - safety check', true);
         }
-        // Hook for post-processing of page content cached/non-cached:
-        $_params = ['pObj' => &$this];
-        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all'] ?? [] as $_funcRef) {
-            GeneralUtility::callUserFunction($_funcRef, $_params, $this);
-        }
-        // Processing if caching is enabled:
-        if (!$this->no_cache) {
-            // Hook for post-processing of page content before being cached:
-            foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-cached'] ?? [] as $_funcRef) {
-                GeneralUtility::callUserFunction($_funcRef, $_params, $this);
-            }
-        }
-        // Storing for cache:
-        if (!$this->no_cache) {
-            $this->realPageCacheContent();
+        $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
+        $event = new AfterCacheableContentIsGeneratedEvent($request, $this, $this->newHash, !$this->no_cache);
+        $event = $eventDispatcher->dispatch($event);
+
+        // Processing if caching is enabled
+        if ($event->isCachingEnabled()) {
+            // Seconds until a cached page is too old
+            $cacheTimeout = $this->get_cache_timeout();
+            $timeOutTime = $GLOBALS['EXEC_TIME'] + $cacheTimeout;
+            // Write the page to cache
+            $cachedInformation = $this->setPageCacheContent($this->content, $this->config, $timeOutTime);
+
+            // Event for cache post processing (eg. writing static files)
+            $event = new AfterCachedPageIsPersistedEvent($request, $this, $this->newHash, $cachedInformation, $cacheTimeout);
+            $eventDispatcher->dispatch($event);
         }
-        // Sets sys-last-change:
         $this->setSysLastChanged();
     }
 
diff --git a/typo3/sysext/frontend/Classes/Event/AfterCacheableContentIsGeneratedEvent.php b/typo3/sysext/frontend/Classes/Event/AfterCacheableContentIsGeneratedEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..8b8fde4f8a36d21d0e61636b42d2582ecc6cacca
--- /dev/null
+++ b/typo3/sysext/frontend/Classes/Event/AfterCacheableContentIsGeneratedEvent.php
@@ -0,0 +1,67 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Frontend\Event;
+
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+
+/**
+ * Event that allows to enhance or change content (also depending if caching is enabled).
+ * Think of $this->isCachingEnabled() as the same as $TSFE->no_cache.
+ * Depending on disable or enabling caching, the cache is then not stored in the pageCache.
+ */
+final class AfterCacheableContentIsGeneratedEvent
+{
+    public function __construct(
+        private readonly ServerRequestInterface $request,
+        private readonly TypoScriptFrontendController $controller,
+        private readonly string $cacheIdentifier,
+        private bool $usePageCache
+    ) {
+    }
+
+    public function getRequest(): ServerRequestInterface
+    {
+        return $this->request;
+    }
+
+    public function getController(): TypoScriptFrontendController
+    {
+        return $this->controller;
+    }
+
+    public function isCachingEnabled(): bool
+    {
+        return $this->usePageCache;
+    }
+
+    public function disableCaching(): void
+    {
+        $this->usePageCache = false;
+    }
+
+    public function enableCaching(): void
+    {
+        $this->usePageCache = true;
+    }
+
+    public function getCacheIdentifier(): string
+    {
+        return $this->cacheIdentifier;
+    }
+}
diff --git a/typo3/sysext/frontend/Classes/Event/AfterCachedPageIsPersistedEvent.php b/typo3/sysext/frontend/Classes/Event/AfterCachedPageIsPersistedEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..0ce5690d6b529e2b89691ae3630c02e267e08bab
--- /dev/null
+++ b/typo3/sysext/frontend/Classes/Event/AfterCachedPageIsPersistedEvent.php
@@ -0,0 +1,69 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Frontend\Event;
+
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+
+/**
+ * Event that is used directly after all cached content is stored in
+ * the page cache.
+ *
+ * If a page is called from the cache, this event is NOT fired.
+ * This event is also NOT FIRED when $TSFE->no_cache (or manipulated via AfterCacheableContentIsGeneratedEvent)
+ * is set.
+ */
+final class AfterCachedPageIsPersistedEvent
+{
+    public function __construct(
+        private readonly ServerRequestInterface $request,
+        private readonly TypoScriptFrontendController $controller,
+        private readonly string $cacheIdentifier,
+        private readonly array $cacheData,
+        private readonly int $cacheLifetime
+    ) {
+    }
+
+    public function getRequest(): ServerRequestInterface
+    {
+        return $this->request;
+    }
+
+    public function getController(): TypoScriptFrontendController
+    {
+        return $this->controller;
+    }
+
+    public function getCacheIdentifier(): string
+    {
+        return $this->cacheIdentifier;
+    }
+
+    public function getCacheData(): array
+    {
+        return $this->cacheData;
+    }
+
+    /**
+     * The amount of seconds until the cache entry is invalid.
+     */
+    public function getCacheLifetime(): int
+    {
+        return $this->cacheLifetime;
+    }
+}
diff --git a/typo3/sysext/frontend/Classes/Http/RequestHandler.php b/typo3/sysext/frontend/Classes/Http/RequestHandler.php
index b54ce368c3ac37cfd96fdb8c7fbe5a402b1e9c0f..8e86c93d03b783639a2bd6758daa8751fb0fa9a2 100644
--- a/typo3/sysext/frontend/Classes/Http/RequestHandler.php
+++ b/typo3/sysext/frontend/Classes/Http/RequestHandler.php
@@ -143,7 +143,7 @@ class RequestHandler implements RequestHandlerInterface
             $this->timeTracker->pull($this->timeTracker->LR ? $controller->content : '');
             $this->timeTracker->decStackPointer();
 
-            $controller->generatePage_postProcessing();
+            $controller->generatePage_postProcessing($request);
             $this->timeTracker->pull();
         }
         $controller->releaseLocks();
@@ -151,7 +151,7 @@ class RequestHandler implements RequestHandlerInterface
         // Render non-cached page parts by replacing placeholders which are taken from cache or added during page generation
         if ($controller->isINTincScript()) {
             if (!$controller->isGeneratePage()) {
-                // When page was generated, this was already called. Avoid calling this twice.
+                // When the page was generated, this was already called. Avoid calling this twice.
                 $controller->preparePageContentGeneration($request);
 
                 // Make sure all FAL resources are prefixed with absPrefPrefix
diff --git a/typo3/sysext/indexed_search/Classes/Hook/TypoScriptFrontendHook.php b/typo3/sysext/indexed_search/Classes/EventListener/FrontendGenerationPageIndexingTrigger.php
similarity index 63%
rename from typo3/sysext/indexed_search/Classes/Hook/TypoScriptFrontendHook.php
rename to typo3/sysext/indexed_search/Classes/EventListener/FrontendGenerationPageIndexingTrigger.php
index 838e647d8d56f3c6c222a34471e4fca051df36ad..8a3ead69eea1f53382d91551904fb254518d2f96 100644
--- a/typo3/sysext/indexed_search/Classes/Hook/TypoScriptFrontendHook.php
+++ b/typo3/sysext/indexed_search/Classes/EventListener/FrontendGenerationPageIndexingTrigger.php
@@ -1,5 +1,7 @@
 <?php
 
+declare(strict_types=1);
+
 /*
  * This file is part of the TYPO3 CMS project.
  *
@@ -13,72 +15,77 @@
  * The TYPO3 project - inspiring people to share!
  */
 
-namespace TYPO3\CMS\IndexedSearch\Hook;
+namespace TYPO3\CMS\IndexedSearch\EventListener;
 
 use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
-use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Context\LanguageAspect;
 use TYPO3\CMS\Core\PageTitle\PageTitleProviderManager;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+use TYPO3\CMS\Frontend\Event\AfterCacheableContentIsGeneratedEvent;
 use TYPO3\CMS\IndexedSearch\Indexer;
 
 /**
- * Hooks for \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController (TSFE).
- * @internal this is a TYPO3-internal hook implementation and not part of TYPO3's Core API.
+ * PSR-14 Event Listener for \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController (TSFE),
+ * which is called just before the content should be stored in the TYPO3 Cache.
+ *
+ * @internal this is a TYPO3-internal Event listener implementation and not part of TYPO3's Core API.
  */
-class TypoScriptFrontendHook
+class FrontendGenerationPageIndexingTrigger
 {
+    public function __construct(
+        protected ExtensionConfiguration $extensionConfiguration,
+        protected TimeTracker $timeTracker,
+        protected PageTitleProviderManager $pageTitleProviderManager,
+        protected Indexer $indexer
+    ) {
+    }
+
     /**
      * Trigger indexing of content, after evaluating if this page could / should be indexed.
-     *
-     * @param array $parameters
-     * @param TypoScriptFrontendController $tsfe
+     * This is triggered for all page content that can be cached.
      */
-    public function indexPageContent(array $parameters, TypoScriptFrontendController $tsfe)
+    public function indexPageContent(AfterCacheableContentIsGeneratedEvent $event): void
     {
+        if (!$event->isCachingEnabled()) {
+            return;
+        }
+        $tsfe = $event->getController();
         // Determine if page should be indexed, and if so, configure and initialize indexer
         if (!($tsfe->config['config']['index_enable'] ?? false)) {
             return;
         }
 
         // Indexer configuration from Extension Manager interface:
-        $disableFrontendIndexing = (bool)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('indexed_search', 'disableFrontendIndexing');
+        $disableFrontendIndexing = (bool)$this->extensionConfiguration->get('indexed_search', 'disableFrontendIndexing');
         $forceIndexing = $tsfe->applicationData['forceIndexing'] ?? false;
 
-        $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
-        $timeTracker->push('Index page');
+        $this->timeTracker->push('Index page');
         if ($disableFrontendIndexing && !$forceIndexing) {
-            $timeTracker->setTSlogMessage('Index page? No, Ordinary Frontend indexing during rendering is disabled.');
+            $this->timeTracker->setTSlogMessage('Index page? No, Ordinary Frontend indexing during rendering is disabled.');
             return;
         }
 
-        if ($tsfe->page['no_search']) {
-            $timeTracker->setTSlogMessage('Index page? No, The "No Search" flag has been set in the page properties!');
+        if ($tsfe->page['no_search'] ?? false) {
+            $this->timeTracker->setTSlogMessage('Index page? No, The "No Search" flag has been set in the page properties!');
             return;
         }
         /** @var LanguageAspect $languageAspect */
-        $languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
+        $languageAspect = $tsfe->getContext()->getAspect('language');
         if ($languageAspect->getId() !== $languageAspect->getContentId()) {
-            $timeTracker->setTSlogMessage('Index page? No, languageId was different from contentId which indicates that the page contains fall-back content and that would be falsely indexed as localized content.');
+            $this->timeTracker->setTSlogMessage('Index page? No, languageId was different from contentId which indicates that the page contains fall-back content and that would be falsely indexed as localized content.');
             return;
         }
         // Init and start indexing
-        $indexer = GeneralUtility::makeInstance(Indexer::class);
-        $indexer->forceIndexing = $forceIndexing;
-        $indexer->init($this->initializeIndexerConfiguration($tsfe, $languageAspect));
-        $indexer->indexTypo3PageContent();
-        $timeTracker->pull();
+        $this->indexer->forceIndexing = $forceIndexing;
+        $this->indexer->init($this->initializeIndexerConfiguration($tsfe, $languageAspect));
+        $this->indexer->indexTypo3PageContent();
+        $this->timeTracker->pull();
     }
 
     /**
      * Setting up internal configuration from config array based on TypoScriptFrontendController
      * Information about page for which the indexing takes place
-     *
-     * @param TypoScriptFrontendController $tsfe
-     * @param LanguageAspect $languageAspect
-     * @return array
      */
     protected function initializeIndexerConfiguration(TypoScriptFrontendController $tsfe, LanguageAspect $languageAspect): array
     {
@@ -93,7 +100,7 @@ class TypoScriptFrontendHook
             // MP variable, if any (Mount Points)
             'MP' => $tsfe->MP,
             // Group list
-            'gr_list' => implode(',', GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('frontend.user', 'groupIds', [0, -1])),
+            'gr_list' => implode(',', $tsfe->getContext()->getPropertyFromAspect('frontend.user', 'groupIds', [0, -1])),
             // page arguments array
             'staticPageArguments' => $pageArguments->getStaticArguments(),
             // The creation date of the TYPO3 page
@@ -106,13 +113,12 @@ class TypoScriptFrontendHook
             $configuration['rootline_uids'][$rlkey] = $rldat['uid'];
         }
         // Content of page
-        $configuration['content'] = $tsfe->content;
         // Content string (HTML of TYPO3 page)
+        $configuration['content'] = $tsfe->content;
 
         // Alternative title for indexing
         // @see https://forge.typo3.org/issues/88041
-        $titleProvider = GeneralUtility::makeInstance(PageTitleProviderManager::class);
-        $configuration['indexedDocTitle'] = $titleProvider->getTitle();
+        $configuration['indexedDocTitle'] = $this->pageTitleProviderManager->getTitle();
 
         // Most recent modification time (seconds) of the content on the page. Used to evaluate whether it should be re-indexed.
         $configuration['mtime'] = $tsfe->register['SYS_LASTCHANGED'] ?? $tsfe->page['SYS_LASTCHANGED'];
diff --git a/typo3/sysext/indexed_search/Configuration/Services.yaml b/typo3/sysext/indexed_search/Configuration/Services.yaml
index 86c1d9c62a3015037b05eca27120d41077815b30..6ee1770bc5381dbb27d19e88fee12f0059b97149 100644
--- a/typo3/sysext/indexed_search/Configuration/Services.yaml
+++ b/typo3/sysext/indexed_search/Configuration/Services.yaml
@@ -12,3 +12,9 @@ services:
       - name: event.listener
         identifier: 'indexed-search'
         method: 'addMysqlFulltextIndex'
+
+  TYPO3\CMS\IndexedSearch\EventListener\FrontendGenerationPageIndexingTrigger:
+    tags:
+      - name: event.listener
+        identifier: 'indexed-search'
+        method: 'indexPageContent'
diff --git a/typo3/sysext/indexed_search/ext_localconf.php b/typo3/sysext/indexed_search/ext_localconf.php
index 39a868f4032dfe1e1b51b8261f8651fd4ffd7881..7c7736f9365d65963a7317c726b62d2535e6458b 100644
--- a/typo3/sysext/indexed_search/ext_localconf.php
+++ b/typo3/sysext/indexed_search/ext_localconf.php
@@ -8,7 +8,6 @@ use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
 use TYPO3\CMS\IndexedSearch\Controller\SearchController;
 use TYPO3\CMS\IndexedSearch\FileContentParser;
 use TYPO3\CMS\IndexedSearch\Hook\DeleteIndexedData;
-use TYPO3\CMS\IndexedSearch\Hook\TypoScriptFrontendHook;
 use TYPO3\CMS\IndexedSearch\Utility\DoubleMetaPhoneUtility;
 
 defined('TYPO3') or die();
@@ -21,7 +20,6 @@ ExtensionUtility::configurePlugin(
     [SearchController::class => 'form,search']
 );
 
-$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-cached']['indexed_search'] = TypoScriptFrontendHook::class . '->indexPageContent';
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval']['indexed_search'] = DeleteIndexedData::class . '->delete';
 
 // Configure default document parsers:
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
index beb003c42317ab62078cc3333783e8443c9025ed..7598197321deec04b29de543c18771a6286f94c0 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
@@ -760,4 +760,28 @@ return [
             'Breaking-97797-GFXSettingProcessor_path_lzwRemoved.rst',
         ],
     ],
+    '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'tslib/class.tslib_fe.php\'][\'contentPostProc-cached\']' => [
+        'restFiles' => [
+            'Breaking-97862-HooksRelatedToGeneratingPageContentRemoved.rst',
+            'Feature-97862-NewPSR-14EventsForManipulatingFrontendPageGenerationAndCacheBehaviour.rst',
+        ],
+    ],
+    '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'tslib/class.tslib_fe.php\'][\'contentPostProc-all\']' => [
+        'restFiles' => [
+            'Breaking-97862-HooksRelatedToGeneratingPageContentRemoved.rst',
+            'Feature-97862-NewPSR-14EventsForManipulatingFrontendPageGenerationAndCacheBehaviour.rst',
+        ],
+    ],
+    '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'tslib/class.tslib_fe.php\'][\'usePageCache\']' => [
+        'restFiles' => [
+            'Breaking-97862-HooksRelatedToGeneratingPageContentRemoved.rst',
+            'Feature-97862-NewPSR-14EventsForManipulatingFrontendPageGenerationAndCacheBehaviour.rst',
+        ],
+    ],
+    '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'tslib/class.tslib_fe.php\'][\'insertPageIncache\']' => [
+        'restFiles' => [
+            'Breaking-97862-HooksRelatedToGeneratingPageContentRemoved.rst',
+            'Feature-97862-NewPSR-14EventsForManipulatingFrontendPageGenerationAndCacheBehaviour.rst',
+        ],
+    ],
 ];
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodArgumentRequiredMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodArgumentRequiredMatcher.php
index 7c6c3833dd4a82b6741501b2b0627f8b0083cf7b..e9a51fb6703c7b852f857577c5be75906d3344e1 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodArgumentRequiredMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodArgumentRequiredMatcher.php
@@ -59,4 +59,11 @@ return [
             'Breaking-96044-HardenMethodSignatureOfLogicalAndAndLogicalOr.rst',
         ],
     ],
+    'TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->generatePage_postProcessing' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 1,
+        'restFiles' => [
+            'Breaking-97862-HooksRelatedToGeneratingPageContentRemoved.rst',
+        ],
+    ],
 ];