diff --git a/.htaccess b/.htaccess
index a69bdd4..0c89072 100644
--- a/.htaccess
+++ b/.htaccess
@@ -99,8 +99,7 @@ DirectoryIndex index.php index.html index.htm
 
   # Redirect common PHP files to their new locations.
   RewriteCond %{REQUEST_URI} ^(.*)?/(update.php) [OR]
-  RewriteCond %{REQUEST_URI} ^(.*)?/(install.php) [OR]
-  RewriteCond %{REQUEST_URI} ^(.*)?/(cron.php)
+  RewriteCond %{REQUEST_URI} ^(.*)?/(install.php)
   RewriteCond %{REQUEST_URI} !core
   RewriteRule ^ %1/core/%2 [L,QSA,R=301]
 
diff --git a/core/INSTALL.txt b/core/INSTALL.txt
index 5c0c7b2..8772d13 100644
--- a/core/INSTALL.txt
+++ b/core/INSTALL.txt
@@ -273,10 +273,10 @@ INSTALLATION
 
    It is also possible to run the cron tasks independent of site visits; this is
    recommended for most sites. To do this, you will need to set up an automated
-   process to visit the page cron.php on your site, which executes the cron
+   process to visit the page /cron on your site, which executes the cron
    tasks.
 
-   The URL of the cron.php page requires a "cron key" to protect against
+   The URL of the cron page requires a "cron key" to protect against
    unauthorized access. Your site's cron key is automatically generated during
    installation and is specific to your site. The full URL of the page, with the
    cron key, is available in the "Cron maintenance tasks" section of the Status
@@ -284,11 +284,11 @@ INSTALLATION
 
    As an example for how to set up this automated process, you can use the
    crontab utility on Unix/Linux systems. The following crontab line uses the
-   wget command to visit the cron.php page, and runs each hour, on the hour:
+   wget command to visit the cron page, and runs each hour, on the hour:
 
-   0 * * * * wget -O - -q -t 1 http://example.com/core/cron.php?cron_key=YOURKEY
+   0 * * * * wget -O - -q -t 1 http://example.com/cron?cron_key=YOURKEY
 
-   Replace the text "http://example.com/core/cron.php?cron_key=YOURKEY" in the
+   Replace the text "http://example.com/cron?cron_key=YOURKEY" in the
    example with the full URL displayed under "Cron maintenance tasks" on the
    "Status report" page.
 
diff --git a/core/cron.php b/core/cron.php
deleted file mode 100644
index fa9aa14..0000000
--- a/core/cron.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-/**
- * @file
- * Handles incoming requests to fire off regularly-scheduled tasks (cron jobs).
- */
-
-// Change the directory to the Drupal root.
-chdir('..');
-
-/**
- * Root directory of Drupal installation.
- */
-define('DRUPAL_ROOT', getcwd());
-
-include_once DRUPAL_ROOT . '/core/includes/bootstrap.inc';
-drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-
-if (!isset($_GET['cron_key']) || variable_get('cron_key', 'drupal') != $_GET['cron_key']) {
-  watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
-  drupal_access_denied();
-}
-elseif (variable_get('maintenance_mode', 0)) {
-  watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
-  drupal_access_denied();
-}
-else {
-  drupal_cron_run();
-}
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 6abe08a..c0db3d7 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -630,7 +630,7 @@ function drupal_settings_initialize() {
     // be modified by a visitor.
     if ($dir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/')) {
       // Remove "core" directory if present, allowing install.php, update.php,
-      // cron.php and others to auto-detect a base path.
+      // and others to auto-detect a base path.
       $core_position = strrpos($dir, '/core');
       if ($core_position !== FALSE && strlen($dir) - 5 == $core_position) {
         $base_path = substr($dir, 0, $core_position);
diff --git a/core/modules/aggregator/aggregator.test b/core/modules/aggregator/aggregator.test
index 61ad16b..8379325 100644
--- a/core/modules/aggregator/aggregator.test
+++ b/core/modules/aggregator/aggregator.test
@@ -801,11 +801,11 @@ class AggregatorCronTestCase extends AggregatorTestCase {
     $key = variable_get('cron_key', 'drupal');
     $this->createSampleNodes();
     $feed = $this->createFeed();
-    $this->drupalGet($base_url . '/core/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key)));
+    $this->cronRun();
     $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.');
     $this->removeFeedItems($feed);
     $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.');
-    $this->drupalGet($base_url . '/core/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key)));
+    $this->cronRun();
     $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.');
 
     // Test feed locking when queued for update.
@@ -816,7 +816,7 @@ class AggregatorCronTestCase extends AggregatorTestCase {
         'queued' => REQUEST_TIME,
       ))
       ->execute();
-    $this->drupalGet($base_url . '/core/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key)));
+    $this->cronRun();
     $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.');
     db_update('aggregator_feed')
       ->condition('fid', $feed->fid)
@@ -824,7 +824,7 @@ class AggregatorCronTestCase extends AggregatorTestCase {
         'queued' => 0,
       ))
       ->execute();
-    $this->drupalGet($base_url . '/core/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key)));
+    $this->cronRun();
     $this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->fid))->fetchField(), 'Expected number of items in database.');
   }
 }
diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php
index 540dc0e..705ce43 100644
--- a/core/modules/simpletest/drupal_web_test_case.php
+++ b/core/modules/simpletest/drupal_web_test_case.php
@@ -2186,7 +2186,7 @@ class DrupalWebTestCase extends DrupalTestCase {
    * Runs cron in the Drupal installed by Simpletest.
    */
   protected function cronRun() {
-    $this->drupalGet($GLOBALS['base_url'] . '/core/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal'))));
+    $this->drupalGet('cron', array('query' => array('cron_key' => variable_get('cron_key', 'drupal'))));
   }
 
   /**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 51603ff..22ff585 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -283,7 +283,7 @@ function system_requirements($phase) {
     }
 
     $description .= ' ' . $t('You can <a href="@cron">run cron manually</a>.', array('@cron' => url('admin/reports/status/run-cron')));
-    $description .= '<br />' . $t('To run cron from outside the site, go to <a href="!cron">!cron</a>', array('!cron' => url($base_url . '/core/cron.php', array('external' => TRUE, 'query' => array('cron_key' => variable_get('cron_key', 'drupal'))))));
+    $description .= '<br />' . $t('To run cron from outside the site, go to <a href="!cron">!cron</a>', array('!cron' => url('cron', array('query' => array('cron_key' => variable_get('cron_key', 'drupal'))))));
 
     $requirements['cron'] = array(
       'title' => $t('Cron maintenance tasks'),
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index dfb8936..e0be8f9 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -5,6 +5,8 @@
  * Configuration system that lets administrators modify the workings of the site.
  */
 
+use Symfony\Component\HttpFoundation\Response;
+
 /**
  * Maximum age of temporary files in seconds.
  */
@@ -580,6 +582,12 @@ function system_element_info() {
  * Implements hook_menu().
  */
 function system_menu() {
+  $items['cron'] = array(
+    'title' => 'Run cron',
+    'page callback' => 'system_cron_callback',
+    'access callback' => 'system_cron_access',
+    'type' => MENU_CALLBACK,
+  );
   $items['system/files'] = array(
     'title' => 'File download',
     'page callback' => 'file_download',
@@ -1129,6 +1137,36 @@ function system_menu() {
 }
 
 /**
+ * Page callback; Execute cron tasks.
+ *
+ * @see system_cron_access().
+ */
+function system_cron_callback() {
+  drupal_cron_run();
+
+  // Returning nothing causes no output to be generated.
+}
+
+/**
+ *Access callback for system_cron().
+ *
+ * @see system_cron_callback().
+ */
+function system_cron_access() {
+  $cron_key = isset($_GET['cron_key']) ? $_GET['cron_key'] : '';
+  if ($cron_key != variable_get('cron_key', 'drupal')) {
+    watchdog('cron', 'Cron could not run because an invalid key was used.', array(), WATCHDOG_NOTICE);
+    return FALSE;
+  }
+  elseif (variable_get('maintenance_mode', 0)) {
+    watchdog('cron', 'Cron could not run because the site is in maintenance mode.', array(), WATCHDOG_NOTICE);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
  * Theme callback for the default batch page.
  */
 function _system_batch_theme() {
diff --git a/core/modules/system/system.test b/core/modules/system/system.test
index 16348cc..fd62a44 100644
--- a/core/modules/system/system.test
+++ b/core/modules/system/system.test
@@ -811,17 +811,17 @@ class CronRunTestCase extends DrupalWebTestCase {
     global $base_url;
 
     // Run cron anonymously without any cron key.
-    $this->drupalGet($base_url . '/core/cron.php', array('external' => TRUE));
+    $this->drupalGet('cron');
     $this->assertResponse(403);
 
     // Run cron anonymously with a random cron key.
     $key = $this->randomName(16);
-    $this->drupalGet($base_url . '/core/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key)));
+    $this->drupalGet('cron', array('query' => array('cron_key' => $key)));
     $this->assertResponse(403);
 
     // Run cron anonymously with the valid cron key.
     $key = variable_get('cron_key', 'drupal');
-    $this->drupalGet($base_url . '/core/cron.php', array('external' => TRUE, 'query' => array('cron_key' => $key)));
+    $this->drupalGet('cron', array('query' => array('cron_key' => $key)));
     $this->assertResponse(200);
   }
 
diff --git a/core/scripts/cron-curl.sh b/core/scripts/cron-curl.sh
index 71f06b9..b36da12 100644
--- a/core/scripts/cron-curl.sh
+++ b/core/scripts/cron-curl.sh
@@ -1,3 +1,3 @@
 #!/bin/sh
 
-curl --silent --compressed http://example.com/core/cron.php
+curl --silent --compressed http://example.com/cron
diff --git a/core/scripts/cron-lynx.sh b/core/scripts/cron-lynx.sh
index 36880d2..516ba65 100644
--- a/core/scripts/cron-lynx.sh
+++ b/core/scripts/cron-lynx.sh
@@ -1,3 +1,3 @@
 #!/bin/sh
 
-/usr/bin/lynx -source http://example.com/core/cron.php > /dev/null 2>&1
+/usr/bin/lynx -source http://example.com/cron > /dev/null 2>&1
diff --git a/core/scripts/drupal.sh b/core/scripts/drupal.sh
index cf17e68..4822d25 100755
--- a/core/scripts/drupal.sh
+++ b/core/scripts/drupal.sh
@@ -43,8 +43,8 @@ All arguments are long options.
               If the given path and file exists it will be executed directly,
               i.e. if URI is set to http://default/bar/foo.php
               and bar/foo.php exists, this script will be executed without
-              bootstrapping Drupal. To execute Drupal's cron.php, specify
-              http://default/core/cron.php as the URI.
+              bootstrapping Drupal. To execute Drupal's update.php, specify
+              http://default/core/update.php as the URI.
 
 
 To run this script without --root argument invoke it from the root directory
