diff --git a/typo3/sysext/core/Classes/Configuration/SiteConfiguration.php b/typo3/sysext/core/Classes/Configuration/SiteConfiguration.php
index 54f0c8b6629fd90d7487e8f3bd7450f4e6469d6f..1da52181a15b21fe576d344ca82e6973573858d5 100644
--- a/typo3/sysext/core/Classes/Configuration/SiteConfiguration.php
+++ b/typo3/sysext/core/Classes/Configuration/SiteConfiguration.php
@@ -158,7 +158,8 @@ class SiteConfiguration implements SingletonInterface
         foreach ($siteConfiguration as $identifier => $configuration) {
             // cast $identifier to string, as the identifier can potentially only consist of (int) digit numbers
             $identifier = (string)$identifier;
-            $siteSettings = new SiteSettings($configuration['settings'] ?? []);
+            $inlineSettings = $configuration['settings'] ?? [];
+            $siteSettings = SiteSettings::createFromSettingsTree($inlineSettings);
             $siteTypoScript = $this->getSiteTypoScript($identifier);
 
             $rootPageId = (int)($configuration['rootPageId'] ?? 0);
diff --git a/typo3/sysext/core/Classes/Site/Entity/Site.php b/typo3/sysext/core/Classes/Site/Entity/Site.php
index a2a0ca0b995fa70f13ca8c8a1d4a6119731727fa..e237fbaeb270c137d18ec82590f6f980876f1cd7 100644
--- a/typo3/sysext/core/Classes/Site/Entity/Site.php
+++ b/typo3/sysext/core/Classes/Site/Entity/Site.php
@@ -96,7 +96,8 @@ class Site implements SiteInterface
         $this->identifier = $identifier;
         $this->rootPageId = $rootPageId;
         if ($settings === null) {
-            $settings = new SiteSettings($configuration['settings'] ?? []);
+            // @todo deprecate null settings argument
+            $settings = SiteSettings::createFromSettingsTree($configuration['settings'] ?? []);
         }
         $this->settings = $settings;
         $this->typoscript = $typoscript;
diff --git a/typo3/sysext/core/Classes/Site/Entity/SiteSettings.php b/typo3/sysext/core/Classes/Site/Entity/SiteSettings.php
index c16bd81205ad096fb26cc0e2db8647ecc77ad598..918efa36a221a47fa7cb5e110fdf0504da40b22a 100644
--- a/typo3/sysext/core/Classes/Site/Entity/SiteSettings.php
+++ b/typo3/sysext/core/Classes/Site/Entity/SiteSettings.php
@@ -28,29 +28,36 @@ use TYPO3\CMS\Core\Utility\ArrayUtility;
 final readonly class SiteSettings extends Settings implements \JsonSerializable
 {
     private array $flatSettings;
+    private array $settingsTree;
 
-    public function __construct(array $settings)
+    public function __construct(array $settings, array $settingsTree, array $flatSettings)
     {
         parent::__construct($settings);
-        $this->flatSettings = $this->isEmpty() ? [] : ArrayUtility::flattenPlain($settings);
+        $this->settingsTree = $settingsTree;
+        $this->flatSettings = $flatSettings;
     }
 
     public function has(string $identifier): bool
     {
-        return isset($this->settings[$identifier]) || isset($this->flatSettings[$identifier]);
+        return isset($this->settings[$identifier]) || isset($this->settingsTree[$identifier]) || isset($this->flatSettings[$identifier]);
     }
 
     public function isEmpty(): bool
     {
-        return $this->settings === [];
+        return $this->settingsTree === [];
     }
 
     public function get(string $identifier, mixed $defaultValue = null): mixed
     {
-        return $this->settings[$identifier] ?? $this->flatSettings[$identifier] ?? $defaultValue;
+        return $this->settings[$identifier] ?? $this->settingsTree[$identifier] ?? $this->flatSettings[$identifier] ?? $defaultValue;
     }
 
     public function getAll(): array
+    {
+        return $this->settingsTree;
+    }
+
+    public function getMap(): array
     {
         return $this->settings;
     }
@@ -65,8 +72,29 @@ final readonly class SiteSettings extends Settings implements \JsonSerializable
         return json_encode($this->settings);
     }
 
-    public static function __set_state(array $state): self
+    /**
+     * @internal
+     */
+    public static function create(array $settingsMap, array $settingsTree): self
+    {
+        $flatSettings = $settingsTree === [] ? [] : ArrayUtility::flattenPlain($settingsTree);
+        return new self(
+            settings: $settingsMap,
+            settingsTree: $settingsTree,
+            flatSettings: $flatSettings,
+        );
+    }
+
+    /**
+     * @internal
+     */
+    public static function createFromSettingsTree(array $settingsTree): self
     {
-        return new self($state['settings'] ?? []);
+        $flatSettings = $settingsTree === [] ? [] : ArrayUtility::flattenPlain($settingsTree);
+        return new self(
+            settings: [],
+            settingsTree: $settingsTree,
+            flatSettings: $flatSettings,
+        );
     }
 }
diff --git a/typo3/sysext/core/Classes/Site/SiteSettingsFactory.php b/typo3/sysext/core/Classes/Site/SiteSettingsFactory.php
index 6ce2e4730d4902343988eda0ab499a341373cb14..5413f36bbabff88205450a9966fdab047c2bc3a2 100644
--- a/typo3/sysext/core/Classes/Site/SiteSettingsFactory.php
+++ b/typo3/sysext/core/Classes/Site/SiteSettingsFactory.php
@@ -59,7 +59,11 @@ readonly class SiteSettingsFactory
         } catch (\Error) {
         }
 
-        $settings = $this->createSettings($siteIdentifier, $siteConfiguration);
+        $settings = $this->createSettings(
+            $siteConfiguration['dependencies'] ?? [],
+            $siteIdentifier,
+            $siteConfiguration['settings'] ?? [],
+        );
         $this->cache->set($cacheIdentifier, 'return ' . var_export($settings, true) . ';');
         return $settings;
     }
@@ -72,12 +76,29 @@ readonly class SiteSettingsFactory
      *       implementing a GUI for the settings - which should either get a dedicated method or a flag to control if
      *       placeholder should be resolved during yaml file loading or not. The SiteConfiguration save action currently
      *       avoid calling this method.
+     * @internal
      */
-    public function createSettings(string $siteIdentifier, array $siteConfiguration): SiteSettings
+    public function createSettings(array $sets = [], ?string $siteIdentifier = null, array $inlineSettings = []): SiteSettings
     {
-        $sets = $siteConfiguration['dependencies'] ?? [];
-        $settings = [];
+        $settingsTree = [];
+        if ($siteIdentifier !== null) {
+            $fileName = $this->configPath . '/' . $siteIdentifier . '/' . $this->settingsFileName;
+            if (file_exists($fileName)) {
+                $settingsTree = $this->yamlFileLoader->load(GeneralUtility::fixWindowsFilePath($fileName));
+            } else {
+                $settingsTree = $inlineSettings;
+            }
+        }
 
+        return $this->composeSettings($settingsTree, $sets);
+    }
+
+    /**
+     * @internal
+     */
+    public function composeSettings(array $settingsTree, array $sets): SiteSettings
+    {
+        $settings = [];
         $definitions = [];
         $activeSets = [];
         if (is_array($sets) && $sets !== []) {
@@ -98,16 +119,20 @@ readonly class SiteSettingsFactory
             ArrayUtility::mergeRecursiveWithOverrule($settings, $this->validateSettings($set->settings, $definitions));
         }
 
-        $fileName = $this->configPath . '/' . $siteIdentifier . '/' . $this->settingsFileName;
-        if (file_exists($fileName)) {
-            $siteSettings = $this->yamlFileLoader->load(GeneralUtility::fixWindowsFilePath($fileName));
-        } else {
-            $siteSettings = $siteConfiguration['settings'] ?? [];
+        if ($settingsTree !== []) {
+            ArrayUtility::mergeRecursiveWithOverrule($settings, $this->validateSettings($settingsTree, $definitions));
         }
 
-        ArrayUtility::mergeRecursiveWithOverrule($settings, $this->validateSettings($siteSettings, $definitions));
+        /** @var array<string, string|int|float|bool|array|null> $settingsMap */
+        $settingsMap = [];
+        foreach ($definitions as $settingDefinition) {
+            $settingsMap[$settingDefinition->key] = ArrayUtility::getValueByPath($settings, $settingDefinition->key, '.');
+        }
 
-        return new SiteSettings($settings);
+        return SiteSettings::create(
+            settingsMap: $settingsMap,
+            settingsTree: $settings,
+        );
     }
 
     protected function validateSettings(array $settings, array $definitions): array
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/Configuration/Sets/Set1/config.yaml b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/Configuration/Sets/Set1/config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..0593413cc9b9bc701f2d3b2c7c8ec9eef5e7176d
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/Configuration/Sets/Set1/config.yaml
@@ -0,0 +1,2 @@
+name: typo3tests/site-settings-set-1
+label: Test Settings Set Fixture 1
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/Configuration/Sets/Set1/settings.definitions.yaml b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/Configuration/Sets/Set1/settings.definitions.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1778627469dacffa10da20da0c5446e8eaebde79
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/Configuration/Sets/Set1/settings.definitions.yaml
@@ -0,0 +1,5 @@
+settings:
+  foo.bar.baz:
+    type: stringlist
+    default: ['a', 'b']
+    label: FooBar
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/composer.json b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..f88efd7bb971876d726818bc51c070431995461f
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/composer.json
@@ -0,0 +1,14 @@
+{
+	"name": "typo3tests/test-site-settings",
+	"type": "typo3-cms-extension",
+	"description": "This extension contains site settings fixtures.",
+	"license": "GPL-2.0-or-later",
+	"require": {
+		"typo3/cms-core": "13.3.*@dev"
+	},
+	"extra": {
+		"typo3/cms": {
+			"extension-key": "test_site_settings"
+		}
+	}
+}
diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/ext_emconf.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/ext_emconf.php
new file mode 100644
index 0000000000000000000000000000000000000000..bb225a67cb7ee99679a4741b86990539a9cdd1ee
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings/ext_emconf.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+$EM_CONF[$_EXTKEY] = [
+    'title' => 'This extension contains site settings fixtures.',
+    'description' => 'This extension contains site settings fixtures.',
+    'category' => 'example',
+    'version' => '13.3.0',
+    'state' => 'beta',
+    'author' => 'Benjamin Franzke',
+    'author_email' => 'ben@bnf.dev',
+    'author_company' => '',
+    'constraints' => [
+        'depends' => [
+            'typo3' => '13.3.0',
+        ],
+        'conflicts' => [],
+        'suggests' => [],
+    ],
+];
diff --git a/typo3/sysext/core/Tests/Functional/Site/Entity/SiteSettingsTest.php b/typo3/sysext/core/Tests/Functional/Site/Entity/SiteSettingsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d5b7225e3d59aa342863171f725ddc0625472bad
--- /dev/null
+++ b/typo3/sysext/core/Tests/Functional/Site/Entity/SiteSettingsTest.php
@@ -0,0 +1,56 @@
+<?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\Core\Tests\Functional\Site\Entity;
+
+use PHPUnit\Framework\Attributes\Test;
+use TYPO3\CMS\Core\Site\SiteSettingsFactory;
+use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
+
+final class SiteSettingsTest extends FunctionalTestCase
+{
+    protected array $testExtensionsToLoad = [
+        'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_site_settings',
+    ];
+
+    #[Test]
+    public function retrieveArraySetting(): void
+    {
+        $siteSettingsFactory = $this->get(SiteSettingsFactory::class);
+        $settings = $siteSettingsFactory->composeSettings([], ['typo3tests/site-settings-set-1']);
+
+        // Available because of definition from settings.definitions.yaml
+        self::assertTrue($settings->has('foo.bar.baz'));
+        self::assertSame(['a', 'b'], $settings->get('foo.bar.baz'));
+        // Available because of legacy array-flattening
+        self::assertTrue($settings->has('foo.bar.baz.0'));
+        self::assertSame('a', $settings->get('foo.bar.baz.0'));
+        self::assertTrue($settings->has('foo.bar.baz.1'));
+        self::assertSame('b', $settings->get('foo.bar.baz.1'));
+    }
+
+    #[Test]
+    public function noAccessUndefinedArray(): void
+    {
+        $siteSettingsFactory = $this->get(SiteSettingsFactory::class);
+        $settings = $siteSettingsFactory->composeSettings([], ['typo3tests/site-settings-set-1']);
+
+        // Not available because there is no settings key named `foo.bar`
+        self::assertFalse($settings->has('foo.bar'));
+        self::assertNull($settings->get('foo.bar'));
+    }
+}
diff --git a/typo3/sysext/core/Tests/Functional/TypoScript/PageTsConfigFactoryTest.php b/typo3/sysext/core/Tests/Functional/TypoScript/PageTsConfigFactoryTest.php
index 79cbc44f03217ced53049147780311186fcd3294..dce9b17a032b140062b90d43ddd57b2cbe71a6ec 100644
--- a/typo3/sysext/core/Tests/Functional/TypoScript/PageTsConfigFactoryTest.php
+++ b/typo3/sysext/core/Tests/Functional/TypoScript/PageTsConfigFactoryTest.php
@@ -157,7 +157,9 @@ final class PageTsConfigFactoryTest extends FunctionalTestCase
             ],
         ];
         $subject = $this->get(PageTsConfigFactory::class);
-        $siteSettings = new SiteSettings(['aSiteSetting' => 'aSiteSettingValue']);
+        $siteSettings = SiteSettings::createFromSettingsTree(
+            ['aSiteSetting' => 'aSiteSettingValue'],
+        );
         $site = new Site('siteIdentifier', 1, [], $siteSettings);
         $pageTsConfig = $subject->create($rootLine, $site);
         self::assertSame('aSiteSettingValue', $pageTsConfig->getPageTsConfigArray()['siteSetting']);