diff --git a/core/core.services.yml b/core/core.services.yml
index 6d0b8c9b6bb74f8aaa61a6e906aaeb213c18e5bd..5e090011e9acf8691b46e7a4f23a82fe34f96797 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -893,10 +893,6 @@ services:
     class: Drupal\Core\StreamWrapper\PublicStream
     tags:
       - { name: stream_wrapper, scheme: public }
-  stream_wrapper.private:
-    class: Drupal\Core\StreamWrapper\PrivateStream
-    tags:
-      - { name: stream_wrapper, scheme: private }
   stream_wrapper.temporary:
     class: Drupal\Core\StreamWrapper\TemporaryStream
     tags:
diff --git a/core/includes/file.inc b/core/includes/file.inc
index b38cf1415488591adeea60ca4ee86303a58297fe..c5e2673f41c2fc231a2ed1c9833a9fb4ae09854c 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -13,6 +13,7 @@
 use Drupal\Core\Site\Settings;
 use Drupal\Core\StreamWrapper\PublicStream;
 use Drupal\Core\StreamWrapper\StreamWrapperInterface;
+use Drupal\Core\StreamWrapper\PrivateStream;
 
 /**
  * Default mode for new directories. See drupal_chmod().
@@ -436,7 +437,7 @@ function file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS)
  */
 function file_ensure_htaccess() {
   file_save_htaccess('public://', FALSE);
-  $private_path = \Drupal::config('system.file')->get('path.private');
+  $private_path = PrivateStream::basePath();
   if (!empty($private_path)) {
     file_save_htaccess('private://', TRUE);
   }
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index 249944f32a45729cff209662727fcf6500825e18..ff821b9bc7ea0893110ff37258c5c7742c84584b 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -21,6 +21,7 @@
 use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass;
 use Drupal\Core\Plugin\PluginManagerPass;
 use Drupal\Core\Render\MainContent\MainContentRenderersPass;
+use Drupal\Core\Site\Settings;
 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 
 /**
@@ -44,6 +45,12 @@ public function register(ContainerBuilder $container) {
     $this->registerUuid($container);
     $this->registerTest($container);
 
+    // Only register the private file stream wrapper if a file path has been set.
+    if (Settings::get('file_private_path')) {
+      $container->register('stream_wrapper.private', 'Drupal\Core\StreamWrapper\PrivateStream')
+        ->addTag('stream_wrapper', ['scheme' => 'private']);
+    }
+
     // Add the compiler pass that lets service providers modify existing
     // service definitions. This pass must come first so that later
     // list-building passes are operating on the post-alter services list.
diff --git a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php
index 84aa2c174aaf2eea2503711579492bce2226be28..f85820dd6e13bbff8247b3b9e198c4e102d1f081 100644
--- a/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php
+++ b/core/lib/Drupal/Core/StreamWrapper/PrivateStream.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\StreamWrapper;
 
 use Drupal\Core\Routing\UrlGeneratorTrait;
+use Drupal\Core\Site\Settings;
 
 /**
  * Drupal private (private://) stream wrapper class.
@@ -41,20 +42,28 @@ public function getDescription() {
   }
 
   /**
-   * Implements Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath()
+   * {@inheritdoc}
    */
   public function getDirectoryPath() {
-    return \Drupal::config('system.file')->get('path.private');
+    return static::basePath();
   }
 
   /**
-   * Implements Drupal\Core\StreamWrapper\StreamWrapperInterface::getExternalUrl().
-   *
-   * @return string
-   *   Returns the HTML URI of a private file.
+   * {@inheritdoc}
    */
-  function getExternalUrl() {
+  public function getExternalUrl() {
     $path = str_replace('\\', '/', $this->getTarget());
     return $this->url('system.private_file_download', ['filepath' => $path], ['absolute' => TRUE]);
   }
+
+  /**
+   * Returns the base path for private://.
+   *
+   * @return string
+   *   The base path for private://.
+   */
+  public static function basePath() {
+    return Settings::get('file_private_path');
+  }
+
 }
diff --git a/core/modules/migrate_drupal/config/install/migrate.migration.d6_system_file.yml b/core/modules/migrate_drupal/config/install/migrate.migration.d6_system_file.yml
index 7515202bfe896a70398a1e3649fb95797372d538..3288591eb45299c9f52c69c34aa19544e537d828 100644
--- a/core/modules/migrate_drupal/config/install/migrate.migration.d6_system_file.yml
+++ b/core/modules/migrate_drupal/config/install/migrate.migration.d6_system_file.yml
@@ -5,11 +5,9 @@ migration_groups:
 source:
   plugin: variable
   variables:
-    - file_directory_path
     - file_directory_temp
     - allow_insecure_uploads
 process:
-  'path/private': file_directory_path
   'path/temporary': file_directory_temp
   allow_insecure_uploads:
     plugin: static_map
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateSystemFileTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateSystemFileTest.php
index a100a0321509a9d12a28e808c97988a8fe89f979..19be586a17e7142616d554cbe0d82adfac829f43 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateSystemFileTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateSystemFileTest.php
@@ -39,7 +39,6 @@ public function testSystemFile() {
     $old_state = \Drupal::configFactory()->getOverrideState();
     \Drupal::configFactory()->setOverrideState(FALSE);
     $config = \Drupal::config('system.file');
-    $this->assertIdentical($config->get('path.private'), 'core/modules/simpletest/files');
     $this->assertIdentical($config->get('path.temporary'), 'files/temp');
     $this->assertIdentical($config->get('allow_insecure_uploads'), TRUE);
     \Drupal::configFactory()->setOverrideState($old_state);
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index 806281e92a1bb300f53fc9624c1577c602a5b0e3..d414afcdc3407c2ad8150b51de94282ccb61984e 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -160,6 +160,15 @@ abstract class TestBase {
    */
   protected $public_files_directory;
 
+  /**
+   * The private file directory for the test environment.
+   *
+   * This is set in TestBase::prepareEnvironment().
+   *
+   * @var string
+   */
+  protected $private_files_directory;
+
   /**
    * Whether to die in case any test assertion fails.
    *
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 77c725b3c386c00f493af0df9fe55124a0cc0bab..1f62ef455d6d416c59a1d0de792aa74c2b4036b1 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -794,6 +794,10 @@ protected function setUp() {
       'value' => $this->public_files_directory,
       'required' => TRUE,
     );
+    $settings['settings']['file_private_path'] = (object) array(
+      'value' => $this->private_files_directory,
+      'required' => TRUE,
+    );
     // Save the original site directory path, so that extensions in the
     // site-specific directory can still be discovered in the test site
     // environment.
@@ -874,7 +878,6 @@ protected function setUp() {
     file_prepare_directory($this->private_files_directory, FILE_CREATE_DIRECTORY);
     file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY);
     $config->get('system.file')
-      ->set('path.private', $this->private_files_directory)
       ->set('path.temporary', $this->temp_files_directory)
       ->save();
 
diff --git a/core/modules/system/config/install/system.file.yml b/core/modules/system/config/install/system.file.yml
index e9f9df0e6f9d5e4cfa1a9bf5f7d8b821f6ddf890..ec8c0533f6837b2baac522c8d6df50b7b37f24df 100644
--- a/core/modules/system/config/install/system.file.yml
+++ b/core/modules/system/config/install/system.file.yml
@@ -1,6 +1,5 @@
 allow_insecure_uploads: false
 default_scheme: 'public'
 path:
-  private: ''
   temporary: ''
 temporary_maximum_age: 21600
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 857b7c7716ef97f487116a4da7d975400df61af6..407d83e6c5c2ab5d12c5d197025c36e2fafbbba3 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -302,9 +302,6 @@ system.file:
       type: mapping
       label: 'Path settings'
       mapping:
-        private:
-          type: string
-          label: 'Private file system path'
         temporary:
           type: string
           label: 'Temporary directory'
diff --git a/core/modules/system/src/Form/FileSystemForm.php b/core/modules/system/src/Form/FileSystemForm.php
index 7c8db2114b17b749c12e95c7fa422129c51aeca3..b5759f3d8e8b7938cbff97f5e4ee753d3fa6188a 100644
--- a/core/modules/system/src/Form/FileSystemForm.php
+++ b/core/modules/system/src/Form/FileSystemForm.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Datetime\DateFormatter;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StreamWrapper\PrivateStream;
 use Drupal\Core\StreamWrapper\PublicStream;
 use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\StreamWrapper\StreamWrapperInterface;
@@ -78,18 +79,15 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     $form['file_public_path'] = array(
       '#type' => 'item',
       '#title' => t('Public file system path'),
-      '#default_value' => PublicStream::basePath(),
       '#markup' => PublicStream::basePath(),
       '#description' => t('A local file system path where public files will be stored. This directory must exist and be writable by Drupal. This directory must be relative to the Drupal installation directory and be accessible over the web. This must be changed in settings.php'),
     );
 
     $form['file_private_path'] = array(
-      '#type' => 'textfield',
+      '#type' => 'item',
       '#title' => t('Private file system path'),
-      '#default_value' => $config->get('path.private'),
-      '#maxlength' => 255,
-      '#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. See the online handbook for <a href="@handbook">more information about securing private files</a>.', array('@handbook' => 'http://drupal.org/documentation/modules/file')),
-      '#after_build' => array('system_check_directory'),
+      '#markup' => (PrivateStream::basePath() ? PrivateStream::basePath() : t('Not set')),
+      '#description' => t('An existing local file system path for storing private files. It should be writable by Drupal and not accessible over the web. This must be changed in settings.php. See the online handbook for <a href="@handbook">more information about securing private files</a>.', array('@handbook' => 'http://drupal.org/documentation/modules/file')),
     );
 
     $form['file_temporary_path'] = array(
@@ -133,7 +131,6 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $config = $this->config('system.file')
-      ->set('path.private', $form_state->getValue('file_private_path'))
       ->set('path.temporary', $form_state->getValue('file_temporary_path'))
       ->set('temporary_maximum_age', $form_state->getValue('temporary_maximum_age'));
 
diff --git a/core/modules/system/src/Tests/File/ConfigTest.php b/core/modules/system/src/Tests/File/ConfigTest.php
index cd849059c41229668a33b2b4b69306fba67df63c..6a0bd6555d0a8c1cbbae41f43681dc60e6ae1fc6 100644
--- a/core/modules/system/src/Tests/File/ConfigTest.php
+++ b/core/modules/system/src/Tests/File/ConfigTest.php
@@ -33,7 +33,6 @@ function testFileConfigurationPage() {
     // upon form submission.
     $file_path = $this->public_files_directory;
     $fields = array(
-      'file_private_path' => $file_path . '/file_config_page_test/private',
       'file_temporary_path' => $file_path . '/file_config_page_test/temporary',
       'file_default_scheme' => 'private',
     );
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 5a1c545f5df235d193f7ce28b335335ce9db5b51..c9ae4c18a3bce60d707f18f3dc0f336e18907ce7 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -12,6 +12,7 @@
 use Drupal\Core\Database\Database;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Site\Settings;
+use Drupal\Core\StreamWrapper\PrivateStream;
 use Drupal\Core\StreamWrapper\PublicStream;
 
 /**
@@ -284,7 +285,7 @@ function system_requirements($phase) {
       'title' => t('Public files directory'),
       'directory' => drupal_realpath('public://'),
     );
-    if (\Drupal::config('system.file')->get('path.private')) {
+    if (PrivateStream::basePath()) {
       $htaccess_files['private://.htaccess'] = array(
         'title' => t('Private files directory'),
         'directory' => drupal_realpath('private://'),
@@ -360,7 +361,7 @@ function system_requirements($phase) {
       PublicStream::basePath(),
       // By default no private files directory is configured. For private files
       // to be secure the admin needs to provide a path outside the webroot.
-      $filesystem_config->get('path.private'),
+      PrivateStream::basePath(),
       file_directory_temp(),
     );
   }
@@ -377,8 +378,8 @@ function system_requirements($phase) {
       // in the intended site directory, so don't require it.
       $directories[] = conf_path(FALSE) . '/files';
     }
-    if (!empty($GLOBALS['config']['system.file']['path']['private'])) {
-      $directories[] = $GLOBALS['config']['system.file']['path']['private'];
+    if ($file_private_path = Settings::get('file_private_path')) {
+      $directories[] = $file_private_path;
     }
     if (!empty($GLOBALS['config']['system.file']['path']['temporary'])) {
       $directories[] = $GLOBALS['config']['system.file']['path']['temporary'];
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 7b75cc9a742ff86283481be627c29cf1ca9be8dd..8f2a8aba2ace73be17e68c2270f8f2423569729c 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -306,16 +306,6 @@ function system_theme_suggestions_field(array $variables) {
   return $suggestions;
 }
 
-/**
- * Implements hook_stream_wrappers_alter().
- */
-function system_stream_wrappers_alter(&$wrappers) {
-  // Only register the private file stream wrapper if a file path has been set.
-  if (!\Drupal::config('system.file')->get('path.private')) {
-    unset($wrappers['private']);
-  }
-}
-
 /**
  * Menu item access callback - only installed themes can be accessed.
  */
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index 6d5f54fc3a5a3227e72d3a84c8ecb4deeed7d895..36385f762076c956157cc5c84478ed5a969f5b27 100644
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -430,6 +430,18 @@
  */
 # $settings['file_public_path'] = 'sites/default/files';
 
+/**
+ * Private file path:
+ *
+ * A local file system path where private files will be stored. This directory
+ * must be absolute, outside of the the Drupal installation directory and not
+ * accessible over the web.
+ *
+ * See http://drupal.org/documentation/modules/file for more information about
+ * securing private files.
+ */
+# $settings['file_private_path'] = '';
+
 /**
  * Session write interval:
  *