diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index 10eb127..40f55f0 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -179,7 +179,55 @@ function path_form_element_validate($element, &$form_state, $complete_form) {
     if ($query->execute()->fetchField()) {
       form_error($element, t('The alias is already in use.'));
     }
+    else {
+      // If it isn't a duplicate, it still might be invalid.
+      $valid = path_alias_is_valid($path['alias']);
+      if (!$valid) {
+        form_error($element, t('The alias is reserved and cannot be used.'));
+      }
+    }
+  }
+}
+
+/**
+ * Validates that a path alias is valid.
+ *
+ * @param $alias
+ *   A string containing the alias in question.
+ *
+ * @return
+ *   TRUE if the alias is valid, FALSE otherwise.
+ */
+function path_alias_is_valid($alias) {
+  $alias = strtolower(trim($alias, '/ '));
+
+  // We could create an alias that would conflict with another node/user/taxonomy
+  // later on, so we need to take account of that. (e.g. Create node 7
+  // with the alias 'node/9/delete'. It will make it impossible to delete
+  // node 9 later on without modifying node 7 first.) We pull it from configuration
+  // so that the user can modify it in their settings.php.
+  $restricted_paths = variable_get('restricted_path_aliases', '/(?:node|taxonomy|user)(?:\/|$)/i');
+  if (preg_match($restricted_paths, $alias)) {
+     return FALSE;
+  }
+
+  // Verify that no other module has reserved this address
+  $router_item = menu_get_item($alias);
+  if (!empty($router_item)) {
+    return FALSE;
   }
+
+  // Before invoking Drupal, the webserver will first check the local
+  // filesystem for files/folders that match the path, so Drupal cannot
+  // match any path that would match an item on the filesystem.
+  // Additionally, .htaccess redirects some D7 paths to their D8 counterparts
+  // (e.g. cron.php). Those have to be enumerated and checked seperately.
+  $restricted_files = array('install.php', 'cron.php', 'update.php');
+  if (file_exists($alias) || in_array($alias, $restricted_files)) {
+    return FALSE;
+  }
+
+  return TRUE;
 }
 
 /**
diff --git a/core/modules/path/path.test b/core/modules/path/path.test
index 4dcd611..9fa8d72 100644
--- a/core/modules/path/path.test
+++ b/core/modules/path/path.test
@@ -212,6 +212,28 @@ class PathAliasTestCase extends PathTestCase {
     $this->assertText(t('The alias is already in use.'));
     $this->assertFieldByXPath("//input[@name='path[alias]' and contains(@class, 'error')]", $edit['path[alias]'], 'Textfield exists and has the error class.');
   }
+
+  /**
+   * Tests that only valid aliases pass path_alias_is_valid().
+   */
+  function testPathAliasIsValid() {
+    $tests = array(
+      'admin' => FALSE,
+      '/Admin/content/' => FALSE,
+      'Update.php' => FALSE,
+      'core/modules' => FALSE,
+      'node/10000/edit' => FALSE,
+      'User' => FALSE,
+      'user/' => FALSE,
+      'user/login' => FALSE,
+      'users_are_people_too' => TRUE,
+      'dries-sings-the-drupal-song' => TRUE,
+    );
+    foreach ($tests AS $alias => $expected) {
+      $result = path_alias_is_valid($alias);
+      $this->assertIdentical($expected, $result);
+    }
+  }
 }
 
 /**
