diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 71895b2..a5a9370 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -14,7 +14,6 @@
 use Drupal\Component\Render\MarkupInterface;
 use Drupal\Core\Test\TestDatabase;
 use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\Site\Settings;
 use Drupal\Core\Utility\Error;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 
diff --git a/core/includes/install.inc b/core/includes/install.inc
index e243c18..5a3bc59 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -342,7 +342,7 @@ function drupal_rewrite_settings($settings = array(), $settings_file = NULL) {
       // Settings singleton.
       if (!empty($settings_settings)) {
         $old_settings = Settings::getAll();
-        new Settings($settings_settings + $old_settings);
+        Settings::setCreateInstance($settings_settings + $old_settings);
       }
       // The existing settings.php file might have been included already. In
       // case an opcode cache is enabled, the rewritten contents of the file
diff --git a/core/lib/Drupal/Core/Cache/CacheFactory.php b/core/lib/Drupal/Core/Cache/CacheFactory.php
index d9b39e4..08e7fdb 100644
--- a/core/lib/Drupal/Core/Cache/CacheFactory.php
+++ b/core/lib/Drupal/Core/Cache/CacheFactory.php
@@ -5,7 +5,7 @@
 /**
  * Defines the cache backend factory.
  */
-use Drupal\Core\Site\Settings;
+use Drupal\Core\Site\Settings\SettingsInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
 
@@ -16,7 +16,7 @@ class CacheFactory implements CacheFactoryInterface, ContainerAwareInterface {
   /**
    * The settings array.
    *
-   * @var \Drupal\Core\Site\Settings
+   * @var \Drupal\Core\Site\Settings\SettingsInterface
    */
   protected $settings;
 
@@ -36,13 +36,13 @@ class CacheFactory implements CacheFactoryInterface, ContainerAwareInterface {
   /**
    * Constructs CacheFactory object.
    *
-   * @param \Drupal\Core\Site\Settings $settings
+   * @param \Drupal\Core\Site\Settings\SettingsInterface $settings
    *   The settings array.
    * @param array $default_bin_backends
    *   (optional) A mapping of bin to backend service name. Mappings in
    *   $settings take precedence over this.
    */
-  public function __construct(Settings $settings, array $default_bin_backends = array()) {
+  public function __construct(SettingsInterface $settings, array $default_bin_backends = array()) {
     $this->settings = $settings;
     $this->defaultBinBackends = $default_bin_backends;
   }
diff --git a/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php b/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php
index 00dc9b5..c15e951 100644
--- a/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php
+++ b/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php
@@ -1,7 +1,8 @@
 <?php
 
 namespace Drupal\Core\Cache;
-use Drupal\Core\Site\Settings;
+
+use Drupal\Core\Site\Settings\SettingsInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
 
 /**
@@ -28,7 +29,7 @@ class ChainedFastBackendFactory implements CacheFactoryInterface {
   /**
    * Constructs ChainedFastBackendFactory object.
    *
-   * @param \Drupal\Core\Site\Settings|null $settings
+   * @param \Drupal\Core\Site\Settings\SettingsInterface|NULL $settings
    *   (optional) The settings object.
    * @param string|null $consistent_service_name
    *   (optional) The service name of the consistent backend factory. Defaults
@@ -40,7 +41,7 @@ class ChainedFastBackendFactory implements CacheFactoryInterface {
    *   - 'cache.backend.apcu' (if the PHP process has APCu enabled)
    *   - NULL (if the PHP process doesn't have APCu enabled)
    */
-  public function __construct(Settings $settings = NULL, $consistent_service_name = NULL, $fast_service_name = NULL) {
+  public function __construct(SettingsInterface $settings = NULL, $consistent_service_name = NULL, $fast_service_name = NULL) {
     // Default the consistent backend to the site's default backend.
     if (!isset($consistent_service_name)) {
       $cache_settings = isset($settings) ? $settings->get('cache') : array();
diff --git a/core/lib/Drupal/Core/Queue/QueueFactory.php b/core/lib/Drupal/Core/Queue/QueueFactory.php
index db493d9..9e00705 100644
--- a/core/lib/Drupal/Core/Queue/QueueFactory.php
+++ b/core/lib/Drupal/Core/Queue/QueueFactory.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\Core\Queue;
 
-use Drupal\Core\Site\Settings;
+use Drupal\Core\Site\Settings\SettingsInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
 
@@ -23,15 +23,17 @@ class QueueFactory implements ContainerAwareInterface {
   /**
    * The settings object.
    *
-   * @var \Drupal\Core\Site\Settings
+   * @var \Drupal\Core\Site\Settings\SettingsInterface
    */
   protected $settings;
 
-
   /**
    * Constructs a queue factory.
+   *
+   * @param \Drupal\Core\Site\Settings\SettingsInterface $settings
+   *   The settings object.
    */
-  function __construct(Settings $settings) {
+  function __construct(SettingsInterface $settings) {
     $this->settings = $settings;
   }
 
diff --git a/core/lib/Drupal/Core/Session/MetadataBag.php b/core/lib/Drupal/Core/Session/MetadataBag.php
index ee0fbd4..c785beb 100644
--- a/core/lib/Drupal/Core/Session/MetadataBag.php
+++ b/core/lib/Drupal/Core/Session/MetadataBag.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\Core\Session;
 
-use Drupal\Core\Site\Settings;
+use Drupal\Core\Site\Settings\SettingsInterface;
 use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag as SymfonyMetadataBag;
 
 /**
@@ -18,10 +18,10 @@ class MetadataBag extends SymfonyMetadataBag {
   /**
    * Constructs a new metadata bag instance.
    *
-   * @param \Drupal\Core\Site\Settings $settings
+   * @param \Drupal\Core\Site\Settings\SettingsInterface $settings
    *   The settings instance.
    */
-  public function __construct(Settings $settings) {
+  public function __construct(SettingsInterface $settings) {
     $update_threshold = $settings->get('session_write_interval', 180);
     parent::__construct('_sf2_meta', $update_threshold);
   }
diff --git a/core/lib/Drupal/Core/Site/Settings.php b/core/lib/Drupal/Core/Site/Settings.php
index 39a7e5a..06066a0 100644
--- a/core/lib/Drupal/Core/Site/Settings.php
+++ b/core/lib/Drupal/Core/Site/Settings.php
@@ -4,37 +4,75 @@
 
 use Drupal\Component\Utility\Crypt;
 use Drupal\Core\Database\Database;
+use Drupal\Core\Site\Settings\PlaceholderSettings;
+use Drupal\Core\Site\Settings\SettingsInstance;
+use Drupal\Core\Site\Settings\SettingsInterface;
+
+/**
+ * Initialize Settings::$instance with a placeholder object.
+ * See https://www.drupal.org/node/2363881
+ *
+ * This technique is the most reliable way to initialize static properties with
+ * non-trivial expressions. It should NOT be used for anything else. Also, the
+ * code being called MUST NOT have any side effects other than initializing the
+ * static properties.
+ *
+ * In general (e.g. in PSR-1), a PHP file should either declare symbols OR have
+ * side-effects, but not both. This specific case is ok only because the side
+ * effect applies to nothing else but the class declared in the same file, and
+ * it happens immediately after the class is being declared. A version of the
+ * class without this initialization applied is never available to the outside
+ * world.
+ *
+ * Note: PHP does not care whether this is called before or after the class
+ * declaration. It is called before only for better visibility.
+ */
 
 /**
  * Read only settings that are initialized with the class.
  *
  * @ingroup utility
  */
-final class Settings {
+abstract class Settings {
 
   /**
-   * Array with the settings.
+   * Singleton instance.
    *
-   * @var array
+   * @var \Drupal\Core\Site\Settings\SettingsInterface
    */
-  private $storage = array();
+  protected static $instance;
 
   /**
    * Singleton instance.
    *
-   * @var \Drupal\Core\Site\Settings
+   * Initializes the static properties. Called once from within the class file.
    */
-  private static $instance;
+  public static function initStaticProperties() {
+    static::$instance = new PlaceholderSettings('Settings::$instance is not initialized yet.');
+  }
 
   /**
-   * Constructor.
+   * Sets or replaces static::$instance with a new Settings object created from a
+   * settings array.
    *
    * @param array $settings
    *   Array with the settings.
+   *   
+   * @return \Drupal\Core\Site\Settings\SettingsInterface
+   *   The new value of static::$instance.
    */
-  public function __construct(array $settings) {
-    $this->storage = $settings;
-    self::$instance = $this;
+  public static function setCreateInstance(array $settings) {
+    static::$instance = new SettingsInstance($settings);
+    return static::$instance;
+  }
+
+  /**
+   * Sets or replaces static::$instance with the given Settings object.
+   *
+   * @param \Drupal\Core\Site\Settings\SettingsInterface $instance
+   */
+  public static function setInstance(SettingsInterface $instance) {
+    static::$instance = $instance;
   }
 
   /**
@@ -43,10 +81,13 @@ public function __construct(array $settings) {
    * A singleton is used because this class is used before the container is
    * available.
    *
-   * @return \Drupal\Core\Site\Settings
+   * @return \Drupal\Core\Site\Settings\SettingsInterface|null
    */
   public static function getInstance() {
-    return self::$instance;
+    if (static::$instance instanceof PlaceholderSettings) {
+      return NULL;
+    }
+    return static::$instance;
   }
 
   /**
@@ -78,7 +119,7 @@ public function __sleep() {
    *   The value of the setting, the provided default if not set.
    */
   public static function get($name, $default = NULL) {
-    return isset(self::$instance->storage[$name]) ? self::$instance->storage[$name] : $default;
+    return static::$instance->get($name, $default);
   }
 
   /**
@@ -88,7 +129,7 @@ public static function get($name, $default = NULL) {
    *   All the settings.
    */
   public static function getAll() {
-    return self::$instance->storage;
+    return static::$instance->getAll();
   }
 
   /**
@@ -120,7 +161,7 @@ public static function initialize($app_root, $site_path, &$class_loader) {
     Database::setMultipleConnectionInfo($databases);
 
     // Initialize Settings.
-    new Settings($settings);
+    static::setCreateInstance($settings);
   }
 
   /**
@@ -132,15 +173,7 @@ public static function initialize($app_root, $site_path, &$class_loader) {
    * @throws \RuntimeException
    */
   public static function getHashSalt() {
-    $hash_salt = self::$instance->get('hash_salt');
-    // This should never happen, as it breaks user logins and many other
-    // services. Therefore, explicitly notify the user (developer) by throwing
-    // an exception.
-    if (empty($hash_salt)) {
-      throw new \RuntimeException('Missing $settings[\'hash_salt\'] in settings.php.');
-    }
-
-    return $hash_salt;
+    return static::$instance->getHashSalt();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php b/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php
index 5208a2d..3bb9eca 100644
--- a/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php
+++ b/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\Core\StackMiddleware;
 
-use Drupal\Core\Site\Settings;
+use Drupal\Core\Site\Settings\SettingsInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 
@@ -21,7 +21,7 @@ class ReverseProxyMiddleware implements HttpKernelInterface {
   /**
    * The site settings.
    *
-   * @var \Drupal\Core\Site\Settings
+   * @var \Drupal\Core\Site\Settings\SettingsInterface
    */
   protected $settings;
 
@@ -30,10 +30,10 @@ class ReverseProxyMiddleware implements HttpKernelInterface {
    *
    * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
    *   The decorated kernel.
-   * @param \Drupal\Core\Site\Settings $settings
+   * @var \Drupal\Core\Site\Settings\SettingsInterface
    *   The site settings.
    */
-  public function __construct(HttpKernelInterface $http_kernel, Settings $settings) {
+  public function __construct(HttpKernelInterface $http_kernel, SettingsInterface $settings) {
     $this->httpKernel = $http_kernel;
     $this->settings = $settings;
   }
diff --git a/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php b/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php
index 9d9a3b3..2e20692 100644
--- a/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php
+++ b/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\Core\StringTranslation\Translator;
 
-use Drupal\Core\Site\Settings;
+use Drupal\Core\Site\Settings\SettingsInterface;
 
 /**
  * String translator using overrides from variables.
@@ -15,17 +15,17 @@ class CustomStrings extends StaticTranslation {
   /**
    * The settings read only object.
    *
-   * @var \Drupal\Core\Site\Settings
+   * @var \Drupal\Core\Site\Settings\SettingsInterface
    */
   protected $settings;
 
   /**
    * Constructs a CustomStrings object.
    *
-   * @param \Drupal\Core\Site\Settings $settings
+   * @param \Drupal\Core\Site\Settings\SettingsInterface $settings
    *   The settings read only object.
    */
-  public function __construct(Settings $settings) {
+  public function __construct(SettingsInterface $settings) {
     parent::__construct();
     $this->settings = $settings;
   }
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index fbf58a5..003d2b6 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -210,7 +210,7 @@ protected function setUp() {
     // Restore and merge settings.
     // DrupalKernel::boot() initializes new Settings, and the containerBuild()
     // method sets additional settings.
-    new Settings($settings + Settings::getAll());
+    Settings::setCreateInstance($settings + Settings::getAll());
 
     // Create and set new configuration directories.
     $this->prepareConfigDirectories();
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index f4a23e7..a459f4a 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -1285,7 +1285,7 @@ private function prepareEnvironment() {
     drupal_valid_test_ua($this->databasePrefix);
 
     // Reset settings.
-    new Settings(array(
+    Settings::setCreateInstance(array(
       // For performance, simply use the database prefix as hash salt.
       'hash_salt' => $this->databasePrefix,
       'container_yamls' => [],
@@ -1378,7 +1378,7 @@ private function restoreEnvironment() {
     // Restore original in-memory configuration.
     $GLOBALS['config'] = $this->originalConfig;
     $GLOBALS['conf'] = $this->originalConf;
-    new Settings($this->originalSettings);
+    Settings::setCreateInstance($this->originalSettings);
 
     // Restore original statics and globals.
     $GLOBALS['config_directories'] = $this->originalConfigDirectories;
@@ -1472,7 +1472,7 @@ protected function exceptionHandler($exception) {
   protected function settingsSet($name, $value) {
     $settings = Settings::getAll();
     $settings[$name] = $value;
-    new Settings($settings);
+    Settings::setCreateInstance($settings);
   }
 
   /**
diff --git a/core/modules/update/src/Access/UpdateManagerAccessCheck.php b/core/modules/update/src/Access/UpdateManagerAccessCheck.php
index 12e34cd..66e161c 100644
--- a/core/modules/update/src/Access/UpdateManagerAccessCheck.php
+++ b/core/modules/update/src/Access/UpdateManagerAccessCheck.php
@@ -4,7 +4,7 @@
 
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Routing\Access\AccessInterface;
-use Drupal\Core\Site\Settings;
+use Drupal\Core\Site\Settings\SettingsInterface;
 
 /**
  * Determines whether allow authorized operations is set.
@@ -14,17 +14,17 @@ class UpdateManagerAccessCheck implements AccessInterface {
   /**
    * Settings Service.
    *
-   * @var \Drupal\Core\Site\Settings
+   * @var \Drupal\Core\Site\Settings\SettingsInterface
    */
   protected $settings;
 
   /**
    * Constructs a UpdateManagerAccessCheck object.
    *
-   * @param \Drupal\Core\Site\Settings $settings
+   * @param \Drupal\Core\Site\Settings\SettingsInterface $settings
    *   The read-only settings container.
    */
-  public function __construct(Settings $settings) {
+  public function __construct(SettingsInterface $settings) {
     $this->settings = $settings;
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Site/SettingsTest.php b/core/tests/Drupal/Tests/Core/Site/SettingsTest.php
index 3cf50fc..004cdc9 100644
--- a/core/tests/Drupal/Tests/Core/Site/SettingsTest.php
+++ b/core/tests/Drupal/Tests/Core/Site/SettingsTest.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\Tests\Core\Site;
 
-use Drupal\Core\Site\Settings;
+use Drupal\Core\Site\Settings\SettingsInterface;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -21,7 +21,7 @@ class SettingsTest extends UnitTestCase {
   /**
    * The class under test.
    *
-   * @var \Drupal\Core\Site\Settings
+   * @var \Drupal\Core\Site\Settings\SettingsInterface
    */
   protected $settings;
 
@@ -61,7 +61,7 @@ public function testGetAll() {
    * @covers ::getInstance
    */
   public function testGetInstance() {
-    $singleton = $this->settings->getInstance();
+    $singleton = Settings::getInstance();
     $this->assertEquals($singleton, $this->settings);
   }
 
@@ -85,7 +85,7 @@ public function testGetHashSalt() {
    */
   public function testGetHashSaltEmpty(array $config) {
     // Re-create settings with no 'hash_salt' key.
-    $settings = new Settings($config);
+    $settings = Settings::setCreateInstance($config);
     $settings->getHashSalt();
   }
 
diff --git a/core/tests/Drupal/Tests/Core/StackMiddleware/ReverseProxyMiddlewareTest.php b/core/tests/Drupal/Tests/Core/StackMiddleware/ReverseProxyMiddlewareTest.php
index 585c4b9..54c1584 100644
--- a/core/tests/Drupal/Tests/Core/StackMiddleware/ReverseProxyMiddlewareTest.php
+++ b/core/tests/Drupal/Tests/Core/StackMiddleware/ReverseProxyMiddlewareTest.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\Tests\Core\StackMiddleware;
 
-use Drupal\Core\Site\Settings;
+use Drupal\Core\Site\Settings\SettingsInterface;
 use Drupal\Core\StackMiddleware\ReverseProxyMiddleware;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\HttpFoundation\Request;
@@ -30,7 +30,7 @@ protected function setUp() {
    * Tests that subscriber does not act when reverse proxy is not set.
    */
   public function testNoProxy() {
-    $settings = new Settings(array());
+    $settings = Settings::setCreateInstance(array());
     $this->assertEquals(0, $settings->get('reverse_proxy'));
 
     $middleware = new ReverseProxyMiddleware($this->mockHttpKernel, $settings);
@@ -50,7 +50,7 @@ public function testNoProxy() {
    */
   public function testReverseProxyEnabled($provided_settings) {
     // Enable reverse proxy and add test values.
-    $settings = new Settings(array('reverse_proxy' => 1) + $provided_settings);
+    $settings = Settings::setCreateInstance(array('reverse_proxy' => 1) + $provided_settings);
     $this->trustedHeadersAreSet($settings);
   }
 
@@ -79,10 +79,10 @@ public function reverseProxyEnabledProvider() {
    * \Symfony\Component\HttpFoundation\Request::setTrustedProxies() should
    * always be called when reverse proxy settings are enabled.
    *
-   * @param \Drupal\Core\Site\Settings $settings
+   * @param \Drupal\Core\Site\Settings\SettingsInterface $settings
    *   The settings object that holds reverse proxy configuration.
    */
-  protected function trustedHeadersAreSet(Settings $settings) {
+  protected function trustedHeadersAreSet(SettingsInterface $settings) {
     $middleware = new ReverseProxyMiddleware($this->mockHttpKernel, $settings);
     $request = new Request();
 
