diff --git a/typo3/sysext/core/Documentation/Changelog/9.5.x/Feature-91354-IntegrateServerResponseSecurityChecks.rst b/typo3/sysext/core/Documentation/Changelog/9.5.x/Feature-91354-IntegrateServerResponseSecurityChecks.rst
new file mode 100644
index 0000000000000000000000000000000000000000..022479ba94604ee29a115fdc87ca534e4784d0b3
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/9.5.x/Feature-91354-IntegrateServerResponseSecurityChecks.rst
@@ -0,0 +1,29 @@
+.. include:: ../../Includes.txt
+
+===========================================================
+Feature: #91354 - Integrate server response security checks
+===========================================================
+
+See :issue:`91354`
+
+Description
+===========
+
+In order to evaluate potential server misconfigurations and to reduce
+the potential of security implications in general, a new HTTP response
+check is integrated to "Environment Status" and the "Security" section
+in the reports module.
+
+
+Impact
+======
+
+It is evaluated whether non-standard file extensions lead to unexpected
+handling on the server-side, such as `test.php.wrong` being evaluated
+as PHP or `test.html.wrong` being served with `text/html` content type.
+
+Details are explained in `TYPO3 Security Guidelines for Administrators`_.
+
+.. _TYPO3 Security Guidelines for Administrators: https://docs.typo3.org/m/typo3/reference-coreapi/10.4/en-us/Security/GuidelinesAdministrators/Index.html#file-extension-handling
+
+.. index:: ext:install
diff --git a/typo3/sysext/install/Classes/Controller/EnvironmentController.php b/typo3/sysext/install/Classes/Controller/EnvironmentController.php
index 364228dc80668cd004cb61edc7981f419fac5566..5d9c5fb8529ebc6c1a1b49700e9059b733027552 100644
--- a/typo3/sysext/install/Classes/Controller/EnvironmentController.php
+++ b/typo3/sysext/install/Classes/Controller/EnvironmentController.php
@@ -42,6 +42,7 @@ use TYPO3\CMS\Install\FolderStructure\DefaultPermissionsCheck;
 use TYPO3\CMS\Install\Service\LateBootService;
 use TYPO3\CMS\Install\SystemEnvironment\Check;
 use TYPO3\CMS\Install\SystemEnvironment\DatabaseCheck;
+use TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck;
 use TYPO3\CMS\Install\SystemEnvironment\SetupCheck;
 
 /**
@@ -135,6 +136,10 @@ class EnvironmentController extends AbstractController
         foreach ($databaseMessages as $message) {
             $messageQueue->enqueue($message);
         }
+        $serverResponseMessages = (new ServerResponseCheck(false))->getStatus();
+        foreach ($serverResponseMessages as $message) {
+            $messageQueue->enqueue($message);
+        }
         return new JsonResponse([
             'success' => true,
             'status' => [
diff --git a/typo3/sysext/install/Classes/Report/SecurityStatusReport.php b/typo3/sysext/install/Classes/Report/SecurityStatusReport.php
index a25471906fca085419ef49a3b6f5e6be8ea793d7..802a4a5ba1f57f951e46ff8d10a237544e259e11 100644
--- a/typo3/sysext/install/Classes/Report/SecurityStatusReport.php
+++ b/typo3/sysext/install/Classes/Report/SecurityStatusReport.php
@@ -22,6 +22,7 @@ use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Install\Service\EnableFileService;
+use TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck;
 use TYPO3\CMS\Reports\Status;
 use TYPO3\CMS\Reports\StatusProviderInterface;
 
@@ -41,7 +42,8 @@ class SecurityStatusReport implements StatusProviderInterface
         $this->executeAdminCommand();
         return [
             'installToolPassword' => $this->getInstallToolPasswordStatus(),
-            'installToolProtection' => $this->getInstallToolProtectionStatus()
+            'installToolProtection' => $this->getInstallToolProtectionStatus(),
+            'serverResponseStatus' => GeneralUtility::makeInstance(ServerResponseCheck::class)->asStatus(),
         ];
     }
 
diff --git a/typo3/sysext/install/Classes/SystemEnvironment/ServerResponse/FileDeclaration.php b/typo3/sysext/install/Classes/SystemEnvironment/ServerResponse/FileDeclaration.php
new file mode 100644
index 0000000000000000000000000000000000000000..3156c4b1b0ad472b8ab68e75a83a04096c50ef3b
--- /dev/null
+++ b/typo3/sysext/install/Classes/SystemEnvironment/ServerResponse/FileDeclaration.php
@@ -0,0 +1,214 @@
+<?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\Install\SystemEnvironment\ServerResponse;
+
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Declares contents on server response expectations on a static file.
+ *
+ * @internal should only be used from within TYPO3 Core
+ */
+class FileDeclaration
+{
+    public const MISMATCH_EXPECTED_CONTENT_TYPE = 'expectedContentType';
+    public const MISMATCH_UNEXPECTED_CONTENT_TYPE = 'unexpectedContentType';
+    public const MISMATCH_EXPECTED_CONTENT = 'expectedContent';
+    public const MISMATCH_UNEXPECTED_CONTENT = 'unexpectedContent';
+    public const FLAG_BUILD_HTML = 1;
+    public const FLAG_BUILD_PHP = 2;
+    public const FLAG_BUILD_SVG = 4;
+    public const FLAG_BUILD_HTML_DOCUMENT = 64;
+    public const FLAG_BUILD_SVG_DOCUMENT = 128;
+
+    /**
+     * @var string
+     */
+    protected $fileName;
+
+    /**
+     * @var bool
+     */
+    protected $fail;
+
+    /**
+     * @var string|null
+     */
+    protected $expectedContentType;
+
+    /**
+     * @var string|null
+     */
+    protected $unexpectedContentType;
+
+    /**
+     * @var string|null
+     */
+    protected $expectedContent;
+
+    /**
+     * @var string|null
+     */
+    protected $unexpectedContent;
+
+    /**
+     * @var int
+     */
+    protected $buildFlags = self::FLAG_BUILD_HTML | self::FLAG_BUILD_HTML_DOCUMENT;
+
+    public function __construct(string $fileName, bool $fail = false)
+    {
+        $this->fileName = $fileName;
+        $this->fail = $fail;
+    }
+
+    public function buildContent(): string
+    {
+        $content = '';
+        if ($this->buildFlags & self::FLAG_BUILD_HTML) {
+            $content .= '<div>HTML content</div>';
+        }
+        if ($this->buildFlags & self::FLAG_BUILD_PHP) {
+            // base64 encoded representation of 'PHP content'
+            $content .= '<div><?php echo base64_decode(\'UEhQIGNvbnRlbnQ=\');?></div>';
+        }
+        if ($this->buildFlags & self::FLAG_BUILD_SVG) {
+            $content .= '<text id="test" x="0" y="0">SVG content</text>';
+        }
+        if ($this->buildFlags & self::FLAG_BUILD_SVG_DOCUMENT) {
+            return sprintf(
+                '<svg xmlns="http://www.w3.org/2000/svg">%s</svg>',
+                $content
+            );
+        }
+        return sprintf(
+            '<!DOCTYPE html><html lang="en"><body>%s</body></html>',
+            $content
+        );
+    }
+
+    public function matches(ResponseInterface $response): bool
+    {
+        return $this->getMismatches($response) === [];
+    }
+
+    public function getMismatches(ResponseInterface $response): array
+    {
+        $mismatches = [];
+        $body = (string)$response->getBody();
+        $contentType = $response->getHeaderLine('content-type');
+        if ($this->expectedContent !== null && strpos($body, $this->expectedContent) === false) {
+            $mismatches[] = self::MISMATCH_EXPECTED_CONTENT;
+        }
+        if ($this->unexpectedContent !== null && strpos($body, $this->unexpectedContent) !== false) {
+            $mismatches[] = self::MISMATCH_UNEXPECTED_CONTENT;
+        }
+        if ($this->expectedContentType !== null
+            && strpos($contentType . ';', $this->expectedContentType . ';') !== 0) {
+            $mismatches[] = self::MISMATCH_EXPECTED_CONTENT_TYPE;
+        }
+        if ($this->unexpectedContentType !== null
+            && strpos($contentType . ';', $this->unexpectedContentType . ';') === 0) {
+            $mismatches[] = self::MISMATCH_UNEXPECTED_CONTENT_TYPE;
+        }
+        return $mismatches;
+    }
+
+    public function withExpectedContentType(string $contentType): self
+    {
+        $target = clone $this;
+        $target->expectedContentType = $contentType;
+        return $target;
+    }
+
+    public function withUnexpectedContentType(string $contentType): self
+    {
+        $target = clone $this;
+        $target->unexpectedContentType = $contentType;
+        return $target;
+    }
+
+    public function withExpectedContent(string $content): self
+    {
+        $target = clone $this;
+        $target->expectedContent = $content;
+        return $target;
+    }
+
+    public function withUnexpectedContent(string $content): self
+    {
+        $target = clone $this;
+        $target->unexpectedContent = $content;
+        return $target;
+    }
+
+    public function withBuildFlags(int $buildFlags): self
+    {
+        $target = clone $this;
+        $target->buildFlags = $buildFlags;
+        return $target;
+    }
+
+    /**
+     * @return string
+     */
+    public function getFileName(): string
+    {
+        return $this->fileName;
+    }
+
+    /**
+     * @return bool
+     */
+    public function shallFail(): bool
+    {
+        return $this->fail;
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getExpectedContentType(): ?string
+    {
+        return $this->expectedContentType;
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getUnexpectedContentType(): ?string
+    {
+        return $this->unexpectedContentType;
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getExpectedContent(): ?string
+    {
+        return $this->expectedContent;
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getUnexpectedContent(): ?string
+    {
+        return $this->unexpectedContent;
+    }
+}
diff --git a/typo3/sysext/install/Classes/SystemEnvironment/ServerResponse/ServerResponseCheck.php b/typo3/sysext/install/Classes/SystemEnvironment/ServerResponse/ServerResponseCheck.php
new file mode 100644
index 0000000000000000000000000000000000000000..bf824bb6a43edfe3804f6318126199c99078397f
--- /dev/null
+++ b/typo3/sysext/install/Classes/SystemEnvironment/ServerResponse/ServerResponseCheck.php
@@ -0,0 +1,313 @@
+<?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\Install\SystemEnvironment\ServerResponse;
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Exception\BadResponseException;
+use function GuzzleHttp\Promise\settle;
+use Psr\Http\Message\ResponseInterface;
+use TYPO3\CMS\Core\Core\Environment;
+use TYPO3\CMS\Core\Messaging\FlashMessage;
+use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\PathUtility;
+use TYPO3\CMS\Install\SystemEnvironment\CheckInterface;
+use TYPO3\CMS\Reports\Status;
+
+/**
+ * Checks how use web server is interpreting static files concerning
+ * their `content-type` and evaluated content in HTTP responses.
+ *
+ * @internal should only be used from within TYPO3 Core
+ */
+class ServerResponseCheck implements CheckInterface
+{
+    /**
+     * @var bool
+     */
+    protected $useMarkup;
+
+    /**
+     * @var FlashMessageQueue
+     */
+    protected $messageQueue;
+
+    /**
+     * @var string
+     */
+    protected $filePath;
+
+    /**
+     * @var string
+     */
+    protected $baseUrl;
+
+    /**
+     * @var FileDeclaration[]
+     */
+    protected $fileDeclarations;
+
+    public function __construct(bool $useMarkup = true)
+    {
+        $this->useMarkup = $useMarkup;
+
+        $fileName = bin2hex(random_bytes(4));
+        $folderName = bin2hex(random_bytes(4));
+        $this->filePath = Environment::getPublicPath()
+            . sprintf('/typo3temp/assets/%s.tmp/', $folderName);
+        $this->baseUrl = GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST')
+            . PathUtility::getAbsoluteWebPath($this->filePath);
+        $this->fileDeclarations = $this->initializeFileDeclarations($fileName);
+    }
+
+    public function asStatus(): Status
+    {
+        $messageQueue = $this->getStatus();
+        $messages = [];
+        foreach ($messageQueue->getAllMessages() as $flashMessage) {
+            $messages[] = $flashMessage->getMessage();
+        }
+        if ($messageQueue->getAllMessages(FlashMessage::ERROR) !== []) {
+            $title = 'Potential vulnerabilities';
+            $severity = Status::ERROR;
+        } elseif ($messageQueue->getAllMessages(FlashMessage::WARNING) !== []) {
+            $title = 'Warnings';
+            $severity = Status::WARNING;
+        }
+        return new Status(
+            'Server Response on static files',
+            $title ?? 'OK',
+            $this->wrapList($messages),
+            $severity ?? Status::OK
+        );
+    }
+
+    public function getStatus(): FlashMessageQueue
+    {
+        $messageQueue = new FlashMessageQueue('install-server-response-check');
+        if (PHP_SAPI === 'cli-server') {
+            $messageQueue->addMessage(
+                new FlashMessage(
+                    'Skipped for PHP_SAPI=cli-server',
+                    'Checks skipped',
+                    FlashMessage::WARNING
+                )
+            );
+            return $messageQueue;
+        }
+        try {
+            $this->buildFileDeclarations();
+            $this->processFileDeclarations($messageQueue);
+            $this->finishMessageQueue($messageQueue);
+        } finally {
+            $this->purgeFileDeclarations();
+        }
+        return $messageQueue;
+    }
+
+    protected function initializeFileDeclarations(string $fileName): array
+    {
+        return [
+            (new FileDeclaration($fileName . '.html'))
+                ->withExpectedContentType('text/html')
+                ->withExpectedContent('HTML content'),
+            (new FileDeclaration($fileName . '.wrong'))
+                ->withUnexpectedContentType('text/html')
+                ->withExpectedContent('HTML content'),
+            (new FileDeclaration($fileName . '.html.wrong'))
+                ->withUnexpectedContentType('text/html')
+                ->withExpectedContent('HTML content'),
+            (new FileDeclaration($fileName . '.1.svg.wrong'))
+                ->withBuildFlags(FileDeclaration::FLAG_BUILD_SVG | FileDeclaration::FLAG_BUILD_SVG_DOCUMENT)
+                ->withUnexpectedContentType('image/svg+xml')
+                ->withExpectedContent('SVG content'),
+            (new FileDeclaration($fileName . '.2.svg.wrong'))
+                ->withBuildFlags(FileDeclaration::FLAG_BUILD_SVG | FileDeclaration::FLAG_BUILD_SVG_DOCUMENT)
+                ->withUnexpectedContentType('image/svg')
+                ->withExpectedContent('SVG content'),
+            (new FileDeclaration($fileName . '.php.wrong', true))
+                ->withBuildFlags(FileDeclaration::FLAG_BUILD_PHP | FileDeclaration::FLAG_BUILD_HTML_DOCUMENT)
+                ->withUnexpectedContent('PHP content'),
+            (new FileDeclaration($fileName . '.html.txt'))
+                ->withExpectedContentType('text/plain')
+                ->withUnexpectedContentType('text/html')
+                ->withExpectedContent('HTML content'),
+            (new FileDeclaration($fileName . '.php.txt', true))
+                ->withBuildFlags(FileDeclaration::FLAG_BUILD_PHP | FileDeclaration::FLAG_BUILD_HTML_DOCUMENT)
+                ->withUnexpectedContent('PHP content'),
+        ];
+    }
+
+    protected function buildFileDeclarations(): void
+    {
+        if (!is_dir($this->filePath)) {
+            GeneralUtility::mkdir_deep($this->filePath);
+        }
+        foreach ($this->fileDeclarations as $fileDeclaration) {
+            file_put_contents(
+                $this->filePath . $fileDeclaration->getFileName(),
+                $fileDeclaration->buildContent()
+            );
+        }
+    }
+
+    protected function purgeFileDeclarations(): void
+    {
+        GeneralUtility::rmdir($this->filePath, true);
+    }
+
+    protected function processFileDeclarations(FlashMessageQueue $messageQueue): void
+    {
+        $promises = [];
+        $client = new Client(['base_uri' => $this->baseUrl]);
+        foreach ($this->fileDeclarations as $fileDeclaration) {
+            $promises[] = $client->requestAsync('GET', $fileDeclaration->getFileName());
+        }
+        foreach (settle($promises)->wait() as $index => $response) {
+            $fileDeclaration = $this->fileDeclarations[$index];
+            if (($response['reason'] ?? null) instanceof BadResponseException) {
+                $messageQueue->addMessage(
+                    new FlashMessage(
+                        sprintf(
+                            '(%d): %s',
+                            $response['reason']->getCode(),
+                            $response['reason']->getRequest()->getUri()
+                        ),
+                        'HTTP warning',
+                        FlashMessage::WARNING
+                    )
+                );
+                continue;
+            }
+            if (!($response['value'] ?? null) instanceof ResponseInterface || $fileDeclaration->matches($response['value'])) {
+                continue;
+            }
+            $messageQueue->addMessage(
+                new FlashMessage(
+                    $this->createMismatchMessage($fileDeclaration, $response['value']),
+                    'Unexpected server response',
+                    $fileDeclaration->shallFail() ? FlashMessage::ERROR : FlashMessage::WARNING
+                )
+            );
+        }
+    }
+
+    protected function finishMessageQueue(FlashMessageQueue $messageQueue): void
+    {
+        if ($messageQueue->getAllMessages(FlashMessage::WARNING) !== []
+            || $messageQueue->getAllMessages(FlashMessage::ERROR) !== []) {
+            return;
+        }
+        $messageQueue->addMessage(
+            new FlashMessage(
+                sprintf('All %d files processed correctly', count($this->fileDeclarations)),
+                'Expected server response',
+                FlashMessage::OK
+            )
+        );
+    }
+
+    protected function createMismatchMessage(FileDeclaration $fileDeclaration, ResponseInterface $response): string
+    {
+        $messageParts = [];
+        $mismatches = $fileDeclaration->getMismatches($response);
+        if (in_array(FileDeclaration::MISMATCH_UNEXPECTED_CONTENT_TYPE, $mismatches, true)) {
+            $messageParts[] = sprintf(
+                'unexpected content-type %s',
+                $this->wrapValue(
+                    $fileDeclaration->getUnexpectedContentType(),
+                    '<code>',
+                    '</code>'
+                )
+            );
+        }
+        if (in_array(FileDeclaration::MISMATCH_EXPECTED_CONTENT_TYPE, $mismatches, true)) {
+            $messageParts[] = sprintf(
+                'content-type mismatch %s, got %s',
+                $this->wrapValue(
+                    $fileDeclaration->getExpectedContent(),
+                    '<code>',
+                    '</code>'
+                ),
+                $this->wrapValue(
+                    $response->getHeaderLine('content-type'),
+                    '<code>',
+                    '</code>'
+                )
+            );
+        }
+        if (in_array(FileDeclaration::MISMATCH_UNEXPECTED_CONTENT, $mismatches, true)) {
+            $messageParts[] = sprintf(
+                'unexpected content %s',
+                $this->wrapValue(
+                    $fileDeclaration->getUnexpectedContent(),
+                    '<code>',
+                    '</code>'
+                )
+            );
+        }
+        if (in_array(FileDeclaration::MISMATCH_EXPECTED_CONTENT, $mismatches, true)) {
+            $messageParts[] = sprintf(
+                'content mismatch %s',
+                $this->wrapValue(
+                    $fileDeclaration->getExpectedContent(),
+                    '<code>',
+                    '</code>'
+                )
+            );
+        }
+        return $this->wrapList(
+            $messageParts,
+            $this->baseUrl . $fileDeclaration->getFileName()
+        );
+    }
+
+    protected function wrapList(array $items, string $label = ''): string
+    {
+        if ($this->useMarkup) {
+            return sprintf(
+                '%s<ul>%s</ul>',
+                $label,
+                implode('', $this->wrapItems($items, '<li>', '</li>'))
+            );
+        }
+        return sprintf(
+            '%s%s',
+            $label ? $label . ': ' : '',
+            implode(', ', $items)
+        );
+    }
+
+    protected function wrapItems(array $items, string $before, string $after): array
+    {
+        return array_map(
+            function (string $item) use ($before, $after): string {
+                return $before . $item . $after;
+            },
+            array_filter($items)
+        );
+    }
+
+    protected function wrapValue(string $value, string $before, string $after): string
+    {
+        if ($this->useMarkup) {
+            return $before . htmlspecialchars($value) . $after;
+        }
+        return $value;
+    }
+}