From 02750689dd25677490f562b0a25e05875bc4c424 Mon Sep 17 00:00:00 2001
From: Dries <dries@buytaert.net>
Date: Sun, 29 Apr 2012 11:16:27 -0400
Subject: [PATCH] - Patch #1183208 by effulgentsia, Niklas Fiekas, katbailey,
 no_commit_credit, catch: remove variable_get()('clean_url()') and switch to
 index.php/path pattern for dirty URL support.

---
 core/authorize.php                            |  11 -
 core/includes/bootstrap.inc                   | 115 ++++++----
 core/includes/common.inc                      | 118 +++++------
 core/includes/file.inc                        |   2 +-
 core/includes/form.inc                        |   4 +-
 core/includes/install.core.inc                |  16 +-
 core/includes/install.inc                     |   3 +-
 core/includes/menu.inc                        |  35 ++--
 core/includes/pager.inc                       |   4 +-
 core/includes/path.inc                        |  33 +--
 core/includes/tablesort.inc                   |   4 +-
 core/includes/theme.inc                       |   2 +-
 core/misc/ajax.js                             |  12 +-
 core/misc/drupal.js                           |   7 +
 core/misc/timezone.js                         |   4 +-
 core/modules/block/block.module               |  11 +-
 core/modules/comment/comment.module           |   8 +-
 core/modules/contact/contact.module           |   2 +-
 core/modules/image/image.module               |   4 +-
 core/modules/image/image.test                 |  10 +-
 core/modules/language/language.admin.inc      |   2 +-
 core/modules/language/language.module         |   2 +-
 .../modules/language/language.negotiation.inc |   9 +-
 core/modules/locale/locale.module             |   2 +-
 core/modules/locale/locale.test               |  20 +-
 core/modules/menu/menu.admin.js               |   3 +-
 core/modules/openid/tests/openid_test.module  |  15 +-
 core/modules/overlay/overlay-parent.js        |  30 +--
 core/modules/overlay/overlay.module           |   2 +-
 core/modules/path/path.admin.inc              |   4 +-
 core/modules/search/search.module             |   2 +-
 core/modules/shortcut/shortcut.admin.inc      |   2 +-
 core/modules/shortcut/shortcut.module         |   2 +-
 .../simpletest/drupal_web_test_case.php       |   2 -
 core/modules/simpletest/simpletest.test       |   2 -
 core/modules/statistics/statistics.module     |   4 +-
 core/modules/statistics/statistics.test       |   9 +-
 core/modules/system/system.admin.inc          |  96 +--------
 core/modules/system/system.api.php            |   2 +-
 core/modules/system/system.cron.js            |   2 +-
 core/modules/system/system.install            |   7 +
 core/modules/system/system.js                 |  49 -----
 core/modules/system/system.maintenance.css    |   7 -
 core/modules/system/system.module             |  22 +-
 core/modules/system/system.test               |   5 +-
 core/modules/system/tests/ajax.test           |   2 +-
 core/modules/system/tests/common.test         | 198 ++++++------------
 core/modules/system/tests/file.test           |  21 +-
 core/modules/system/tests/menu.test           |   4 +-
 .../tests/modules/menu_test/menu_test.module  |   2 +-
 .../url_alter_test/url_alter_test.module      |   4 +-
 core/modules/system/tests/path.test           |   6 +-
 core/modules/system/tests/session.test        |   4 +-
 core/modules/system/tests/tablesort.test      |   7 +-
 core/modules/system/tests/theme.test          |  14 +-
 .../modules/system/tests/upgrade/upgrade.test |   2 -
 core/modules/taxonomy/taxonomy.admin.inc      |   4 +-
 core/modules/update/update.compare.inc        |   3 +-
 core/modules/update/update.module             |   2 +-
 core/modules/user/user.module                 |   4 +-
 core/scripts/drupal.sh                        |   7 +-
 core/scripts/run-tests.sh                     |   2 +-
 robots.txt                                    |  18 +-
 web.config                                    |   7 +-
 64 files changed, 374 insertions(+), 644 deletions(-)

diff --git a/core/authorize.php b/core/authorize.php
index c6ba51dbf4da..d703b332c858 100644
--- a/core/authorize.php
+++ b/core/authorize.php
@@ -103,17 +103,6 @@ function authorize_access_allowed() {
   // Load the code that drives the authorize process.
   require_once DRUPAL_ROOT . '/core/includes/authorize.inc';
 
-  // For the sake of Batch API and a few other low-level functions, we need to
-  // initialize the URL path into $_GET['q']. However, we do not want to raise
-  // our bootstrap level, nor do we want to call drupal_initialize_path(),
-  // since that is assuming that modules are loaded and invoking hooks.
-  // However, all we really care is if we're in the middle of a batch, in which
-  // case $_GET['q'] will already be set, we just initialize it to an empty
-  // string if it's not already defined.
-  if (!isset($_GET['q'])) {
-    $_GET['q'] = '';
-  }
-
   if (isset($_SESSION['authorize_operation']['page_title'])) {
     drupal_set_title($_SESSION['authorize_operation']['page_title']);
   }
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 6abe08a571b3..c3768825cd86 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -551,12 +551,8 @@ function drupal_environment_initialize() {
     $_SERVER['HTTP_HOST'] = '';
   }
 
-  // When clean URLs are enabled, emulate ?q=foo/bar using REQUEST_URI. It is
-  // not possible to append the query string using mod_rewrite without the B
-  // flag (this was added in Apache 2.2.8), because mod_rewrite unescapes the
-  // path before passing it on to PHP. This is a problem when the path contains
-  // e.g. "&" or "%" that have special meanings in URLs and must be encoded.
-  $_GET['q'] = request_path();
+  // @todo Refactor with the Symfony Request object.
+  _current_path(request_path());
 
   // Enforce E_STRICT, but allow users to set levels not part of E_STRICT.
   error_reporting(E_STRICT | E_ALL | error_reporting());
@@ -597,7 +593,7 @@ function drupal_valid_http_host($host) {
  * Sets the base URL, cookie domain, and session name from configuration.
  */
 function drupal_settings_initialize() {
-  global $base_url, $base_path, $base_root;
+  global $base_url, $base_path, $base_root, $script_path;
 
   // Export the following settings.php variables to the global namespace
   global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url, $config_directory_name, $config_signature_key;
@@ -626,8 +622,8 @@ function drupal_settings_initialize() {
 
     $base_url = $base_root;
 
-    // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
-    // be modified by a visitor.
+    // For a request URI of '/index.php/foo', $_SERVER['SCRIPT_NAME'] is
+    // '/index.php', whereas $_SERVER['PHP_SELF'] is '/index.php/foo'.
     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.
@@ -648,6 +644,32 @@ function drupal_settings_initialize() {
   $base_secure_url = str_replace('http://', 'https://', $base_url);
   $base_insecure_url = str_replace('https://', 'http://', $base_url);
 
+  // Determine the path of the script relative to the base path, and add a
+  // trailing slash. This is needed for creating URLs to Drupal pages.
+  if (!isset($script_path)) {
+    $script_path = '';
+    // We don't expect scripts outside of the base path, but sanity check
+    // anyway.
+    if (strpos($_SERVER['SCRIPT_NAME'], $base_path) === 0) {
+      $script_path = substr($_SERVER['SCRIPT_NAME'], strlen($base_path)) . '/';
+      // If the request URI does not contain the script name, then clean URLs
+      // are in effect and the script path can be similarly dropped from URL
+      // generation. For servers that don't provide $_SERVER['REQUEST_URI'], we
+      // do not know the actual URI requested by the client, and request_uri()
+      // returns a URI with the script name, resulting in non-clean URLs unless
+      // there's other code that intervenes.
+      if (strpos(request_uri(TRUE) . '/', $base_path . $script_path) !== 0) {
+        $script_path = '';
+      }
+      // @todo Temporary BC for install.php, update.php, and other scripts.
+      //   - http://drupal.org/node/1547184
+      //   - http://drupal.org/node/1546082
+      if ($script_path !== 'index.php/') {
+        $script_path = '';
+      }
+    }
+  }
+
   if ($cookie_domain) {
     // If the user specifies the cookie domain, also use it for session name.
     $session_name = $cookie_domain;
@@ -1488,13 +1510,15 @@ function drupal_validate_utf8($text) {
  *
  * Because $_SERVER['REQUEST_URI'] is only available on Apache, we generate an
  * equivalent using other environment variables.
+ *
+ * @todo The above comment is incorrect: http://drupal.org/node/1547294.
  */
-function request_uri() {
+function request_uri($omit_query_string = FALSE) {
   if (isset($_SERVER['REQUEST_URI'])) {
     $uri = $_SERVER['REQUEST_URI'];
   }
   else {
-    if (isset($_SERVER['argv'])) {
+    if (isset($_SERVER['argv'][0])) {
       $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0];
     }
     elseif (isset($_SERVER['QUERY_STRING'])) {
@@ -1507,7 +1531,7 @@ function request_uri() {
   // Prevent multiple slashes to avoid cross site requests via the Form API.
   $uri = '/' . ltrim($uri, '/');
 
-  return $uri;
+  return $omit_query_string ? strtok($uri, '?') : $uri;
 }
 
 /**
@@ -2181,7 +2205,7 @@ function _drupal_bootstrap_page_cache() {
     if (is_object($cache)) {
       header('X-Drupal-Cache: HIT');
       // Restore the metadata cached with the page.
-      $_GET['q'] = $cache->data['path'];
+      _current_path($cache->data['path']);
       drupal_set_title($cache->data['title'], PASS_THROUGH);
       date_default_timezone_set(drupal_get_user_timezone());
       // If the skipping of the bootstrap hooks is not enforced, call
@@ -2418,9 +2442,9 @@ function drupal_maintenance_theme() {
  */
 function drupal_fast_404() {
   $exclude_paths = variable_get('404_fast_paths_exclude', FALSE);
-  if ($exclude_paths && !preg_match($exclude_paths, $_GET['q'])) {
+  if ($exclude_paths && !preg_match($exclude_paths, request_path())) {
     $fast_paths = variable_get('404_fast_paths', FALSE);
-    if ($fast_paths && preg_match($fast_paths, $_GET['q'])) {
+    if ($fast_paths && preg_match($fast_paths, request_path())) {
       drupal_add_http_header('Status', '404 Not Found');
       $fast_404_html = variable_get('404_fast_html', '<html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>');
       // Replace @path in the variable with the page path.
@@ -2655,41 +2679,44 @@ function request_path() {
     return $path;
   }
 
-  if (isset($_GET['q'])) {
-    // This is a request with a ?q=foo/bar query string. $_GET['q'] is
-    // overwritten in drupal_path_initialize(), but request_path() is called
-    // very early in the bootstrap process, so the original value is saved in
-    // $path and returned in later calls.
-    $path = $_GET['q'];
-  }
-  elseif (isset($_SERVER['REQUEST_URI'])) {
-    // This request is either a clean URL, or 'index.php', or nonsense.
-    // Extract the path from REQUEST_URI.
-    $request_path = strtok($_SERVER['REQUEST_URI'], '?');
-    $base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/'));
-    // Unescape and strip $base_path prefix, leaving q without a leading slash.
-    $path = substr(urldecode($request_path), $base_path_len + 1);
-    // If the path equals the script filename, either because 'index.php' was
-    // explicitly provided in the URL, or because the server added it to
-    // $_SERVER['REQUEST_URI'] even when it wasn't provided in the URL (some
-    // versions of Microsoft IIS do this), the front page should be served.
-    if ($path == basename($_SERVER['PHP_SELF'])) {
-      $path = '';
-    }
-  }
-  else {
-    // This is the front page.
+  // Get the part of the URI between the base path of the Drupal installation
+  // and the query string, and unescape it.
+  $request_path = request_uri(TRUE);
+  $base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/'));
+  $path = substr(urldecode($request_path), $base_path_len + 1);
+
+  // Depending on server configuration, the URI might or might not include the
+  // script name. For example, the front page might be accessed as
+  // http://example.com or as http://example.com/index.php, and the "user"
+  // page might be accessed as http://example.com/user or as
+  // http://example.com/index.php/user. Strip the script name from $path.
+  $script = basename($_SERVER['SCRIPT_NAME']);
+  if ($path == $script) {
     $path = '';
   }
+  elseif (strpos($path, $script . '/') === 0) {
+    $path = substr($path, strlen($script) + 1);
+  }
 
-  // Under certain conditions Apache's RewriteRule directive prepends the value
-  // assigned to $_GET['q'] with a slash. Moreover we can always have a trailing
-  // slash in place, hence we need to normalize $_GET['q'].
+  // Extra slashes can appear in URLs or under some conditions, added by Apache,
+  // so normalize.
   $path = trim($path, '/');
 
   return $path;
 }
 
+/**
+ * @todo This is a temporary function pending refactoring Drupal to use
+ *   Symfony's Request object exclusively.
+ */
+function _current_path($path = NULL) {
+  static $current_path = '';
+  if (isset($path)) {
+    $current_path = $path;
+  }
+  return $current_path;
+}
+
 /**
  * Returns a component of the current Drupal path.
  *
@@ -2726,7 +2753,9 @@ function arg($index = NULL, $path = NULL) {
   $arguments = &$drupal_static_fast['arguments'];
 
   if (!isset($path)) {
-    $path = $_GET['q'];
+    // @todo The public function current_path() is not available during early
+    //   bootstrap.
+    $path = _current_path();
   }
   if (!isset($arguments[$path])) {
     $arguments[$path] = explode('/', $path);
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 353a9b51d8c9..17c626b2b6be 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -397,14 +397,14 @@ function drupal_get_feeds($delimiter = "\n") {
  *   (optional) An array to be processed. Defaults to $_GET.
  * @param $exclude
  *   (optional) A list of $query array keys to remove. Use "parent[child]" to
- *   exclude nested items. Defaults to array('q').
+ *   exclude nested items.
  * @param $parent
  *   Internal use only. Used to build the $query array key for nested items.
  *
  * @return
  *   An array containing query parameters, which can be used for url().
  */
-function drupal_get_query_parameters(array $query = NULL, array $exclude = array('q'), $parent = '') {
+function drupal_get_query_parameters(array $query = NULL, array $exclude = array(), $parent = '') {
   // Set defaults, if none given.
   if (!isset($query)) {
     $query = $_GET;
@@ -517,7 +517,7 @@ function drupal_get_destination() {
     $destination = array('destination' => $_GET['destination']);
   }
   else {
-    $path = $_GET['q'];
+    $path = current_path();
     $query = drupal_http_build_query(drupal_get_query_parameters());
     if ($query != '') {
       $path .= '?' . $query;
@@ -598,13 +598,6 @@ function drupal_parse_url($url) {
       $options['fragment'] = $parts['fragment'];
     }
   }
-  // The 'q' parameter contains the path of the current page if clean URLs are
-  // disabled. It overrides the 'path' of the URL when present, even if clean
-  // URLs are enabled, due to how Apache rewriting rules work.
-  if (isset($options['query']['q'])) {
-    $options['path'] = $options['query']['q'];
-    unset($options['query']['q']);
-  }
 
   return $options;
 }
@@ -2138,14 +2131,9 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
  *     dependent URL requires so.
  *   - 'prefix': Only used internally, to modify the path when a language
  *     dependent URL requires so.
- *   - 'script': The script filename in Drupal's root directory to use when
- *     clean URLs are disabled, such as 'index.php'. Defaults to an empty
- *     string, as most modern web servers automatically find 'index.php'. If
- *     clean URLs are disabled, the value of $path is appended as query
- *     parameter 'q' to $options['script'] in the returned URL. When deploying
- *     Drupal on a web server that cannot be configured to automatically find
- *     index.php, then hook_url_outbound_alter() can be implemented to force
- *     this value to 'index.php'.
+ *   - 'script': Added to the URL between the base path and the path prefix.
+ *     Defaults to empty string when clean URLs are in effect, and to
+ *     'index.php/' when they are not.
  *   - 'entity_type': The entity type of the object that called url(). Only
  *     set if url() is invoked by entity_uri().
  *   - 'entity': The entity object (such as a node) for which the URL is being
@@ -2161,7 +2149,8 @@ function url($path = NULL, array $options = array()) {
     'query' => array(),
     'absolute' => FALSE,
     'alias' => FALSE,
-    'prefix' => ''
+    'prefix' => '',
+    'script' => $GLOBALS['script_path'],
   );
 
   if (!isset($options['external'])) {
@@ -2243,32 +2232,9 @@ function url($path = NULL, array $options = array()) {
   $base = $options['absolute'] ? $options['base_url'] . '/' : base_path();
   $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
 
-  // With Clean URLs.
-  if (!empty($GLOBALS['conf']['clean_url'])) {
-    $path = drupal_encode_path($prefix . $path);
-    if ($options['query']) {
-      return $base . $path . '?' . drupal_http_build_query($options['query']) . $options['fragment'];
-    }
-    else {
-      return $base . $path . $options['fragment'];
-    }
-  }
-  // Without Clean URLs.
-  else {
-    $path = $prefix . $path;
-    $query = array();
-    if (!empty($path)) {
-      $query['q'] = $path;
-    }
-    if ($options['query']) {
-      // We do not use array_merge() here to prevent overriding $path via query
-      // parameters.
-      $query += $options['query'];
-    }
-    $query = $query ? ('?' . drupal_http_build_query($query)) : '';
-    $script = isset($options['script']) ? $options['script'] : '';
-    return $base . $script . $query . $options['fragment'];
-  }
+  $path = drupal_encode_path($prefix . $path);
+  $query = $options['query'] ? ('?' . drupal_http_build_query($options['query'])) : '';
+  return $base . $options['script'] . $path . $query . $options['fragment'];
 }
 
 /**
@@ -2404,7 +2370,7 @@ function l($text, $path, array $options = array()) {
   );
 
   // Append active class.
-  if (($path == $_GET['q'] || ($path == '<front>' && drupal_is_front_page())) &&
+  if (($path == current_path() || ($path == '<front>' && drupal_is_front_page())) &&
       (empty($options['language']) || $options['language']->langcode == drupal_container()->get(LANGUAGE_TYPE_URL)->langcode)) {
     $options['attributes']['class'][] = 'active';
   }
@@ -2530,7 +2496,7 @@ function drupal_deliver_page($page_callback_result, $default_delivery_callback =
     // If a delivery callback is specified, but doesn't exist as a function,
     // something is wrong, but don't print anything, since it's not known
     // what format the response needs to be in.
-    watchdog('delivery callback not found', 'callback %callback not found: %q.', array('%callback' => $delivery_callback, '%q' => $_GET['q']), WATCHDOG_ERROR);
+    watchdog('delivery callback not found', 'callback %callback not found: %path.', array('%callback' => $delivery_callback, '%path' => current_path()), WATCHDOG_ERROR);
   }
 }
 
@@ -2571,18 +2537,18 @@ function drupal_deliver_html_page($page_callback_result) {
         // Print a 404 page.
         drupal_add_http_header('Status', '404 Not Found');
 
-        watchdog('page not found', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
+        watchdog('page not found', check_plain(current_path()), NULL, WATCHDOG_WARNING);
 
         // Check for and return a fast 404 page if configured.
         drupal_fast_404();
 
         // Keep old path for reference, and to allow forms to redirect to it.
         if (!isset($_GET['destination'])) {
-          $_GET['destination'] = $_GET['q'];
+          $_GET['destination'] = current_path();
         }
 
         $path = drupal_get_normal_path(variable_get('site_404', ''));
-        if ($path && $path != $_GET['q']) {
+        if ($path && $path != current_path()) {
           // Custom 404 handler. Set the active item in case there are tabs to
           // display, or other dependencies on the path.
           menu_set_active_item($path);
@@ -2603,15 +2569,15 @@ function drupal_deliver_html_page($page_callback_result) {
       case MENU_ACCESS_DENIED:
         // Print a 403 page.
         drupal_add_http_header('Status', '403 Forbidden');
-        watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
+        watchdog('access denied', check_plain(current_path()), NULL, WATCHDOG_WARNING);
 
         // Keep old path for reference, and to allow forms to redirect to it.
         if (!isset($_GET['destination'])) {
-          $_GET['destination'] = $_GET['q'];
+          $_GET['destination'] = current_path();
         }
 
         $path = drupal_get_normal_path(variable_get('site_403', ''));
-        if ($path && $path != $_GET['q']) {
+        if ($path && $path != current_path()) {
           // Custom 403 handler. Set the active item in case there are tabs to
           // display or other dependencies on the path.
           menu_set_active_item($path);
@@ -3570,11 +3536,15 @@ function drupal_build_css_cache($css) {
     if (!file_exists($uri) && !file_unmanaged_save_data($data, $uri, FILE_EXISTS_REPLACE)) {
       return FALSE;
     }
-    // If CSS gzip compression is enabled, clean URLs are enabled (which means
-    // that rewrite rules are working) and the zlib extension is available then
-    // create a gzipped version of this file. This file is served conditionally
-    // to browsers that accept gzip using .htaccess rules.
-    if (variable_get('css_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib')) {
+    // If CSS gzip compression is enabled and the zlib extension is available
+    // then create a gzipped version of this file. This file is served
+    // conditionally to browsers that accept gzip using .htaccess rules.
+    // It's possible that the rewrite rules in .htaccess aren't working on this
+    // server, but there's no harm (other than the time spent generating the
+    // file) in generating the file anyway. Sites on servers where rewrite rules
+    // aren't working can set css_gzip_compression to FALSE in order to skip
+    // generating a file that won't be used.
+    if (variable_get('css_gzip_compression', TRUE) && extension_loaded('zlib')) {
       if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) {
         return FALSE;
       }
@@ -4105,15 +4075,19 @@ function drupal_add_js($data = NULL, $options = NULL) {
     // Add jquery.js and drupal.js, as well as the basePath setting, the
     // first time a JavaScript file is added.
     if (empty($javascript)) {
-      // url() generates the prefix using hook_url_outbound_alter(). Instead of
-      // running the hook_url_outbound_alter() again here, extract the prefix
-      // from url().
-      url('', array('prefix' => &$prefix));
+      // url() generates the script and prefix using hook_url_outbound_alter().
+      // Instead of running the hook_url_outbound_alter() again here, extract
+      // them from url().
+      // @todo Make this less hacky: http://drupal.org/node/1547376.
+      $scriptPath = $GLOBALS['script_path'];
+      $pathPrefix = '';
+      url('', array('script' => &$scriptPath, 'prefix' => &$pathPrefix));
       $javascript = array(
         'settings' => array(
           'data' => array(
             array('basePath' => base_path()),
-            array('pathPrefix' => empty($prefix) ? '' : $prefix),
+            array('scriptPath' => $scriptPath),
+            array('pathPrefix' => $pathPrefix),
           ),
           'type' => 'setting',
           'scope' => 'header',
@@ -5056,11 +5030,15 @@ function drupal_build_js_cache($files) {
     if (!file_exists($uri) && !file_unmanaged_save_data($contents, $uri, FILE_EXISTS_REPLACE)) {
       return FALSE;
     }
-    // If JS gzip compression is enabled, clean URLs are enabled (which means
-    // that rewrite rules are working) and the zlib extension is available then
-    // create a gzipped version of this file. This file is served conditionally
-    // to browsers that accept gzip using .htaccess rules.
-    if (variable_get('js_gzip_compression', TRUE) && variable_get('clean_url', 0) && extension_loaded('zlib')) {
+    // If JS gzip compression is enabled and the zlib extension is available
+    // then create a gzipped version of this file. This file is served
+    // conditionally to browsers that accept gzip using .htaccess rules.
+    // It's possible that the rewrite rules in .htaccess aren't working on this
+    // server, but there's no harm (other than the time spent generating the
+    // file) in generating the file anyway. Sites on servers where rewrite rules
+    // aren't working can set js_gzip_compression to FALSE in order to skip
+    // generating a file that won't be used.
+    if (variable_get('js_gzip_compression', TRUE) && extension_loaded('zlib')) {
       if (!file_exists($uri . '.gz') && !file_unmanaged_save_data(gzencode($contents, 9, FORCE_GZIP), $uri . '.gz', FILE_EXISTS_REPLACE)) {
         return FALSE;
       }
@@ -5217,7 +5195,7 @@ function _drupal_bootstrap_full() {
     ini_set('error_log', 'public://error.log');
   }
 
-  // Initialize $_GET['q'] prior to invoking hook_init().
+  // Initialize current_path() prior to invoking hook_init().
   drupal_path_initialize();
 
   // Let all modules take action before the menu system handles the request.
@@ -5256,7 +5234,7 @@ function drupal_page_set_cache() {
     $cache = (object) array(
       'cid' => $base_root . request_uri(),
       'data' => array(
-        'path' => $_GET['q'],
+        'path' => current_path(),
         'body' => ob_get_clean(),
         'title' => drupal_get_title(),
         'headers' => array(),
diff --git a/core/includes/file.inc b/core/includes/file.inc
index c4356a937a3a..b476bc78ddb5 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -2031,7 +2031,7 @@ function file_transfer($uri, $headers) {
  * @see system_menu()
  */
 function file_download() {
-  // Merge remainder of arguments from GET['q'], into relative file path.
+  // Merge remaining path arguments into relative file path.
   $args = func_get_args();
   $scheme = array_shift($args);
   $target = implode('/', $args);
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 6d93420ceedf..bc71b9ef2d5f 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -1248,7 +1248,7 @@ function drupal_redirect_form($form_state) {
         $function($form_state['redirect']);
       }
     }
-    drupal_goto($_GET['q']);
+    drupal_goto(current_path());
   }
 }
 
@@ -4682,7 +4682,7 @@ function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = 'd
       'progressive' => TRUE,
       'url' => $url,
       'url_options' => array(),
-      'source_url' => $_GET['q'],
+      'source_url' => current_path(),
       'redirect' => $redirect,
       'theme' => $GLOBALS['theme_key'],
       'redirect_callback' => $redirect_callback,
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 125852a9e567..5cb3399a094a 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1522,11 +1522,8 @@ function install_configure_form($form, &$form_state, &$install_state) {
   // We add these strings as settings because JavaScript translation does not
   // work on install time.
   drupal_add_js(array('copyFieldValue' => array('edit-site-mail' => array('edit-account-mail'))), 'setting');
-  drupal_add_js('jQuery(function () { Drupal.cleanURLsInstallCheck(); });', 'inline');
   // Add JS to show / hide the 'Email administrator about site updates' elements
   drupal_add_js('jQuery(function () { Drupal.hideEmailAdministratorCheckbox() });', 'inline');
-  // Build menu to allow clean URL check.
-  menu_rebuild();
 
   // Cache a fully-built schema. This is necessary for any invocation of
   // index.php because: (1) setting cache table entries requires schema
@@ -1534,8 +1531,7 @@ function install_configure_form($form, &$form_state, &$install_state) {
   // loaded, so (3) if there is no cached schema, drupal_get_schema() will
   // try to generate one but with no loaded modules will return nothing.
   //
-  // This logically could be done during the 'install_finished' task, but the
-  // clean URL check requires it now.
+  // @todo Move this to the 'install_finished' task?
   drupal_get_schema(NULL, TRUE);
 
   // Return the form.
@@ -1827,12 +1823,6 @@ function _install_configure_form($form, &$form_state, &$install_state) {
     '#attributes' => array('class' => array('timezone-detect')),
   );
 
-  $form['server_settings']['clean_url'] = array(
-    '#type' => 'hidden',
-    '#default_value' => 0,
-    '#attributes' => array('id' => 'edit-clean-url', 'class' => array('install')),
-  );
-
   $form['update_notifications'] = array(
     '#type' => 'fieldset',
     '#title' => st('Update notifications'),
@@ -1903,10 +1893,6 @@ function install_configure_form_submit($form, &$form_state) {
   $user = user_load(1);
   user_login_finalize();
 
-  if (isset($form_state['values']['clean_url'])) {
-    variable_set('clean_url', $form_state['values']['clean_url']);
-  }
-
   // Record when this install ran.
   variable_set('install_time', $_SERVER['REQUEST_TIME']);
 }
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 72adf1ca16f0..283e31ca7b25 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -121,8 +121,7 @@ function drupal_detect_baseurl($file = 'core/install.php') {
   $proto = $_SERVER['HTTPS'] ? 'https://' : 'http://';
   $host = $_SERVER['SERVER_NAME'];
   $port = ($_SERVER['SERVER_PORT'] == 80 ? '' : ':' . $_SERVER['SERVER_PORT']);
-  $uri = preg_replace("/\?.*/", '', $_SERVER['REQUEST_URI']);
-  $dir = str_replace("/$file", '', $uri);
+  $dir = str_replace("/$file", '', $_SERVER['SCRIPT_NAME']);
 
   return "$proto$host$port$dir";
 }
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index 96791e39ef78..2bfc39e6e89a 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -441,7 +441,7 @@ function menu_set_item($path, $router_item) {
 function menu_get_item($path = NULL, $router_item = NULL) {
   $router_items = &drupal_static(__FUNCTION__);
   if (!isset($path)) {
-    $path = $_GET['q'];
+    $path = current_path();
   }
   if (isset($router_item)) {
     $router_items[$path] = $router_item;
@@ -498,7 +498,7 @@ function menu_execute_active_handler($path = NULL, $deliver = TRUE) {
   // Allow other modules to change the site status but not the path because that
   // would not change the global variable. hook_url_inbound_alter() can be used
   // to change the path. Code later will not use the $read_only_path variable.
-  $read_only_path = !empty($path) ? $path : $_GET['q'];
+  $read_only_path = !empty($path) ? $path : current_path();
   drupal_alter('menu_site_status', $page_callback_result, $read_only_path);
 
   // Only continue if the site status is not set.
@@ -1043,11 +1043,11 @@ function menu_tree_output($tree) {
       $class[] = 'active-trail';
       $data['link']['localized_options']['attributes']['class'][] = 'active-trail';
     }
-    // Normally, l() compares the href of every link with $_GET['q'] and sets
-    // the active class accordingly. But local tasks do not appear in menu
+    // Normally, l() compares the href of every link with the current path and
+    // sets the active class accordingly. But local tasks do not appear in menu
     // trees, so if the current path is a local task, and this link is its
     // tab root, then we have to set the class manually.
-    if ($data['link']['href'] == $router_item['tab_root_href'] && $data['link']['href'] != $_GET['q']) {
+    if ($data['link']['href'] == $router_item['tab_root_href'] && $data['link']['href'] != current_path()) {
       $data['link']['localized_options']['attributes']['class'][] = 'active';
     }
 
@@ -1846,11 +1846,11 @@ function menu_navigation_links($menu_name, $level = 0) {
         $class = ' active-trail';
         $l['attributes']['class'][] = 'active-trail';
       }
-      // Normally, l() compares the href of every link with $_GET['q'] and sets
-      // the active class accordingly. But local tasks do not appear in menu
-      // trees, so if the current path is a local task, and this link is its
-      // tab root, then we have to set the class manually.
-      if ($item['link']['href'] == $router_item['tab_root_href'] && $item['link']['href'] != $_GET['q']) {
+      // Normally, l() compares the href of every link with the current path and
+      // sets the active class accordingly. But local tasks do not appear in
+      // menu trees, so if the current path is a local task, and this link is
+      // its tab root, then we have to set the class manually.
+      if ($item['link']['href'] == $router_item['tab_root_href'] && $item['link']['href'] != current_path()) {
         $l['attributes']['class'][] = 'active';
       }
       // Keyed with the unique mlid to generate classes in theme_links().
@@ -1964,8 +1964,8 @@ function menu_local_tasks($level = 0) {
             // local tasks link to their parent, but the path of default local
             // tasks can still be accessed directly, in which case this link
             // would not be marked as active, since l() only compares the href
-            // with $_GET['q'].
-            if ($link['href'] != $_GET['q']) {
+            // with current_path().
+            if ($link['href'] != current_path()) {
               $link['localized_options']['attributes']['class'][] = 'active';
             }
             $tabs_current[] = array(
@@ -2040,8 +2040,8 @@ function menu_local_tasks($level = 0) {
             // Mark the link as active, if the current path is a (second-level)
             // local task of a default local task. Since this default local task
             // links to its parent, l() will not mark it as active, as it only
-            // compares the link's href to $_GET['q'].
-            if ($link['href'] != $_GET['q']) {
+            // compares the link's href to current_path().
+            if ($link['href'] != current_path()) {
               $link['localized_options']['attributes']['class'][] = 'active';
             }
             $tabs_current[] = array(
@@ -2304,10 +2304,11 @@ function menu_get_active_menu_names() {
  *   A Drupal path - not a path alias.
  */
 function menu_set_active_item($path) {
-  $_GET['q'] = $path;
   // Since the active item has changed, the active menu trail may also be out
   // of date.
   drupal_static_reset('menu_set_active_trail');
+  // @todo Refactor to use the Symfony Request object.
+  _current_path($path);
 }
 
 /**
@@ -2421,7 +2422,7 @@ function menu_link_get_preferred($path = NULL, $selected_menu = NULL) {
   $preferred_links = &drupal_static(__FUNCTION__);
 
   if (!isset($path)) {
-    $path = $_GET['q'];
+    $path = current_path();
   }
 
   if (empty($selected_menu)) {
@@ -3813,7 +3814,7 @@ function _menu_site_is_offline($check_only = FALSE) {
       // Ensure that the maintenance mode message is displayed only once
       // (allowing for page redirects) and specifically suppress its display on
       // the maintenance mode settings page.
-      if (!$check_only && $_GET['q'] != 'admin/config/development/maintenance') {
+      if (!$check_only && current_path() != 'admin/config/development/maintenance') {
         if (user_access('administer site configuration')) {
           drupal_set_message(t('Operating in maintenance mode. <a href="@url">Go online.</a>', array('@url' => url('admin/config/development/maintenance'))), 'status', FALSE);
         }
diff --git a/core/includes/pager.inc b/core/includes/pager.inc
index c579e7e64809..201c5ad6f213 100644
--- a/core/includes/pager.inc
+++ b/core/includes/pager.inc
@@ -297,7 +297,7 @@ function pager_default_initialize($total, $limit, $element = 0) {
 function pager_get_query_parameters() {
   $query = &drupal_static(__FUNCTION__);
   if (!isset($query)) {
-    $query = drupal_get_query_parameters($_GET, array('q', 'page'));
+    $query = drupal_get_query_parameters($_GET, array('page'));
   }
   return $query;
 }
@@ -638,7 +638,7 @@ function theme_pager_link($variables) {
   //   none of the pager links is active at any time - but it should still be
   //   possible to use l() here.
   // @see http://drupal.org/node/1410574
-  $attributes['href'] = url($_GET['q'], array('query' => $query));
+  $attributes['href'] = url(current_path(), array('query' => $query));
   return '<a' . drupal_attributes($attributes) . '>' . check_plain($text) . '</a>';
 }
 
diff --git a/core/includes/path.inc b/core/includes/path.inc
index d6a0201fbe8c..0c7e4d40ff0a 100644
--- a/core/includes/path.inc
+++ b/core/includes/path.inc
@@ -10,15 +10,24 @@
  */
 
 /**
- * Initialize the $_GET['q'] variable to the proper normal path.
+ * Initializes the current path to the proper normal path.
  */
 function drupal_path_initialize() {
-  // Ensure $_GET['q'] is set before calling drupal_normal_path(), to support
-  // path caching with hook_url_inbound_alter().
-  if (empty($_GET['q'])) {
-    $_GET['q'] = variable_get('site_frontpage', 'user');
+  // At this point, the current path is either the request path (due to
+  // drupal_environment_initialize()) or some modified version of it due to
+  // other bootstrap code (e.g., language negotiation), but it has not yet been
+  // normalized by drupal_get_normal_path().
+  $path = _current_path();
+
+  // If on the front page, resolve to the front page path, including for calls
+  // to current_path() while drupal_get_normal_path() is in progress.
+  if (empty($path)) {
+    $path = variable_get('site_frontpage', 'user');
+    _current_path($path);
   }
-  $_GET['q'] = drupal_get_normal_path($_GET['q']);
+
+  // Normalize the path.
+  _current_path(drupal_get_normal_path($path));
 }
 
 /**
@@ -234,7 +243,7 @@ function drupal_cache_system_paths() {
 function drupal_get_path_alias($path = NULL, $langcode = NULL) {
   // If no path is specified, use the current page's path.
   if ($path == NULL) {
-    $path = $_GET['q'];
+    $path = current_path();
   }
   $result = $path;
   if ($alias = drupal_lookup_path('alias', $path, $langcode)) {
@@ -289,9 +298,7 @@ function drupal_is_front_page() {
   $is_front_page = &$drupal_static_fast['is_front_page'];
 
   if (!isset($is_front_page)) {
-    // As drupal_path_initialize updates $_GET['q'] with the 'site_frontpage' path,
-    // we can check it against the 'site_frontpage' variable.
-    $is_front_page = ($_GET['q'] == variable_get('site_frontpage', 'user'));
+    $is_front_page = (current_path() == variable_get('site_frontpage', 'user'));
   }
 
   return $is_front_page;
@@ -340,9 +347,9 @@ function drupal_match_path($path, $patterns) {
  * - http://example.com/path/alias (which is a path alias for node/306) returns
  *   "node/306" as opposed to the path alias.
  *
- * This function is not available in hook_boot() so use $_GET['q'] instead.
+ * This function is not available in hook_boot() so use request_path() instead.
  * However, be careful when doing that because in the case of Example #3
- * $_GET['q'] will contain "path/alias". If "node/306" is needed, calling
+ * request_path() will contain "path/alias". If "node/306" is needed, calling
  * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL) makes this function available.
  *
  * @return
@@ -351,7 +358,7 @@ function drupal_match_path($path, $patterns) {
  * @see request_path()
  */
 function current_path() {
-  return $_GET['q'];
+  return _current_path();
 }
 
 /**
diff --git a/core/includes/tablesort.inc b/core/includes/tablesort.inc
index 3c70b965c30a..a900a612fe73 100644
--- a/core/includes/tablesort.inc
+++ b/core/includes/tablesort.inc
@@ -150,7 +150,7 @@ function tablesort_header($cell, $header, $ts) {
       $ts['sort'] = 'asc';
       $image = '';
     }
-    $cell['data'] = l($cell['data'] . $image, $_GET['q'], array('attributes' => array('title' => $title), 'query' => array_merge($ts['query'], array('sort' => $ts['sort'], 'order' => $cell['data'])), 'html' => TRUE));
+    $cell['data'] = l($cell['data'] . $image, current_path(), array('attributes' => array('title' => $title), 'query' => array_merge($ts['query'], array('sort' => $ts['sort'], 'order' => $cell['data'])), 'html' => TRUE));
 
     unset($cell['field'], $cell['sort']);
   }
@@ -193,7 +193,7 @@ function tablesort_cell($cell, $header, $ts, $i) {
  *   page request except for those pertaining to table sorting.
  */
 function tablesort_get_query_parameters() {
-  return drupal_get_query_parameters($_GET, array('q', 'sort', 'order'));
+  return drupal_get_query_parameters($_GET, array('sort', 'order'));
 }
 
 /**
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 99e872ed8544..aadafd8c08e9 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1743,7 +1743,7 @@ function theme_links($variables) {
 
       // Handle links.
       if (isset($link['href'])) {
-        $is_current_path = ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()));
+        $is_current_path = ($link['href'] == current_path() || ($link['href'] == '<front>' && drupal_is_front_page()));
         $is_current_language = (empty($link['language']) || $link['language']->langcode == $language_url->langcode);
         if ($is_current_path && $is_current_language) {
           $class[] = 'active';
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index c35241c94d82..072cd2bf6603 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -115,16 +115,12 @@ Drupal.ajax = function (base, element, element_settings) {
 
   // Replacing 'nojs' with 'ajax' in the URL allows for an easy method to let
   // the server detect when it needs to degrade gracefully.
-  // There are five scenarios to check for:
+  // There are four scenarios to check for:
   // 1. /nojs/
   // 2. /nojs$ - The end of a URL string.
-  // 3. /nojs? - Followed by a query (with clean URLs enabled).
-  //      E.g.: path/nojs?destination=foobar
-  // 4. /nojs& - Followed by a query (without clean URLs enabled).
-  //      E.g.: ?q=path/nojs&destination=foobar
-  // 5. /nojs# - Followed by a fragment.
-  //      E.g.: path/nojs#myfragment
-  this.url = element_settings.url.replace(/\/nojs(\/|$|\?|&|#)/g, '/ajax$1');
+  // 3. /nojs? - Followed by a query (e.g. path/nojs?destination=foobar).
+  // 4. /nojs# - Followed by a fragment (e.g.: path/nojs#myfragment).
+  this.url = element_settings.url.replace(/\/nojs(\/|$|\?|#)/g, '/ajax$1');
   this.wrapper = '#' + element_settings.wrapper;
 
   // If there isn't a form, jQuery.ajax() will be used instead, allowing us to
diff --git a/core/misc/drupal.js b/core/misc/drupal.js
index c7917b07e54b..5bbbf511023e 100644
--- a/core/misc/drupal.js
+++ b/core/misc/drupal.js
@@ -201,6 +201,13 @@ Drupal.t = function (str, args, options) {
   return str;
 };
 
+/**
+ * Returns the URL to a Drupal page.
+ */
+Drupal.url = function (path) {
+  return Drupal.settings.basePath + Drupal.settings.scriptPath + path;
+}
+
 /**
  * Format a string containing a count of items.
  *
diff --git a/core/misc/timezone.js b/core/misc/timezone.js
index 62b7d4b0219a..6f82bc1d3eae 100644
--- a/core/misc/timezone.js
+++ b/core/misc/timezone.js
@@ -50,8 +50,8 @@ Drupal.behaviors.setTimezone = {
       var element = this;
       $.ajax({
         async: false,
-        url: settings.basePath,
-        data: { q: path, date: dateString },
+        url: Drupal.url(path),
+        data: { date: dateString },
         dataType: 'json',
         success: function (data) {
           if (data) {
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 488904ca182f..2bfd7653b9c1 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -836,13 +836,10 @@ function block_block_list_alter(&$blocks) {
       // with different case. Ex: /Page, /page, /PAGE.
       $pages = drupal_strtolower($block->pages);
       if ($block->visibility < BLOCK_VISIBILITY_PHP) {
-        // Convert the Drupal path to lowercase
-        $path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
-        // Compare the lowercase internal and lowercase path alias (if any).
-        $page_match = drupal_match_path($path, $pages);
-        if ($path != $_GET['q']) {
-          $page_match = $page_match || drupal_match_path($_GET['q'], $pages);
-        }
+        // Compare the lowercase path alias (if any) and internal path.
+        $path = current_path();
+        $path_alias = drupal_strtolower(drupal_get_path_alias($path));
+        $page_match = drupal_match_path($path_alias, $pages) || (($path != $path_alias) && drupal_match_path($path, $pages));
         // When $block->visibility has a value of 0 (BLOCK_VISIBILITY_NOTLISTED),
         // the block is displayed on all pages except those listed in $block->pages.
         // When set to 1 (BLOCK_VISIBILITY_LISTED), it is displayed only on those
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 03029131b1e2..c43e72540f2b 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -500,12 +500,10 @@ function comment_permalink($cid) {
     // Find the current display page for this comment.
     $page = comment_get_display_page($comment->cid, $node->type);
 
-    // Set $_GET['q'] and $_GET['page'] ourselves so that the node callback
-    // behaves as it would when visiting the page directly.
-    $_GET['q'] = 'node/' . $node->nid;
-    $_GET['page'] = $page;
-
     // Return the node view, this will show the correct comment in context.
+    // @todo Refactor to use Symfony's Request object.
+    _current_path('node/' . $node->nid);
+    $_GET['page'] = $page;
     return menu_execute_active_handler('node/' . $node->nid, FALSE);
   }
   drupal_not_found();
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index c695c7e2eaf3..48c12d90f615 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -173,7 +173,7 @@ function contact_mail($key, &$message, $params) {
     '!site-name' => variable_get('site_name', 'Drupal'),
     '!subject' => $params['subject'],
     '!category' => isset($params['category']['category']) ? $params['category']['category'] : '',
-    '!form-url' => url($_GET['q'], array('absolute' => TRUE, 'language' => $language)),
+    '!form-url' => url(current_path(), array('absolute' => TRUE, 'language' => $language)),
     '!sender-name' => user_format_name($params['sender']),
     '!sender-url' => $params['sender']->uid ? url('user/' . $params['sender']->uid, array('absolute' => TRUE, 'language' => $language)) : $params['sender']->mail,
   );
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 9e13c85735c9..8fd8ceeb6991 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -869,10 +869,10 @@ function image_style_url($style_name, $path) {
   $uri = image_style_path($style_name, $path);
 
   // If not using clean URLs, the image derivative callback is only available
-  // with the query string. If the file does not exist, use url() to ensure
+  // with the script path. If the file does not exist, use url() to ensure
   // that it is included. Once the file exists it's fine to fall back to the
   // actual file path, this avoids bootstrapping PHP once the files are built.
-  if (!variable_get('clean_url') && file_uri_scheme($uri) == 'public' && !file_exists($uri)) {
+  if ($GLOBALS['script_path'] && file_uri_scheme($uri) == 'public' && !file_exists($uri)) {
     $directory_path = file_stream_wrapper_get_instance_by_uri($uri)->getDirectoryPath();
     return url($directory_path . '/' . file_uri_target($uri), array('absolute' => TRUE));
   }
diff --git a/core/modules/image/image.test b/core/modules/image/image.test
index d3b3374d1a9a..ff783be31188 100644
--- a/core/modules/image/image.test
+++ b/core/modules/image/image.test
@@ -184,10 +184,12 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase {
    * Test image_style_url().
    */
   function _testImageStyleUrlAndPath($scheme, $clean_url = TRUE) {
+    $script_path_original = $GLOBALS['script_path'];
+    $GLOBALS['script_path'] = $clean_url ? '' : 'index.php/';
+
     // Make the default scheme neither "public" nor "private" to verify the
     // functions work for other than the default scheme.
     variable_set('file_default_scheme', 'temporary');
-    variable_set('clean_url', $clean_url);
 
     // Create the directories for the styles.
     $directory = $scheme . '://styles/' . $this->style_name;
@@ -209,8 +211,8 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase {
     $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
     $generate_url = image_style_url($this->style_name, $original_uri);
 
-    if (!$clean_url) {
-      $this->assertTrue(strpos($generate_url, '?q=') !== FALSE, 'When using non-clean URLS, the system path contains the query string.');
+    if ($GLOBALS['script_path']) {
+      $this->assertTrue(strpos($generate_url, $GLOBALS['script_path']) !== FALSE, 'When using non-clean URLS, the system path contains the script name.');
     }
 
     // Fetch the URL that generates the file.
@@ -224,6 +226,8 @@ class ImageStylesPathAndUrlUnitTest extends DrupalWebTestCase {
     if ($scheme == 'private') {
       $this->assertEqual($this->drupalGetHeader('X-Image-Owned-By'), 'image_module_test', t('Expected custom header has been added.'));
     }
+
+    $GLOBALS['script_path'] = $script_path_original;
   }
 }
 
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index b05a1306e967..c124b9e32b52 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -679,7 +679,7 @@ function language_negotiation_configure_url_form($form, &$form_state) {
       '#title' => t('%language (%langcode) path prefix', array('%language' => $language->name, '%langcode' => $language->langcode)),
       '#maxlength' => 64,
       '#default_value' => isset($prefixes[$langcode]) ? $prefixes[$langcode] : '',
-      '#field_prefix' => $base_url . '/' . (variable_get('clean_url', 0) ? '' : '?q='),
+      '#field_prefix' => $base_url . '/',
     );
     $form['domain'][$langcode] = array(
       '#type' => 'textfield',
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 63b3743e9638..c7d6f1822d21 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -463,7 +463,7 @@ function language_block_info() {
  */
 function language_block_view($type) {
   if (language_multilingual()) {
-    $path = drupal_is_front_page() ? '<front>' : $_GET['q'];
+    $path = drupal_is_front_page() ? '<front>' : current_path();
     $links = language_negotiation_get_switch_links($type, $path);
 
     if (isset($links->links)) {
diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc
index 6269e8b203e6..4d560e7d361d 100644
--- a/core/modules/language/language.negotiation.inc
+++ b/core/modules/language/language.negotiation.inc
@@ -209,9 +209,11 @@ function language_from_url($languages) {
 
   switch (variable_get('language_negotiation_url_part', LANGUAGE_NEGOTIATION_URL_PREFIX)) {
     case LANGUAGE_NEGOTIATION_URL_PREFIX:
-      // $_GET['q'] might not be available at this time, because path
-      // initialization runs after the language bootstrap phase.
-      list($language, $_GET['q']) = language_url_split_prefix(isset($_GET['q']) ? $_GET['q'] : NULL, $languages);
+      // Language negotiation happens before the public function current_path()
+      // is available.
+      // @todo Refactor with Symfony's Request object.
+      list($language, $path) = language_url_split_prefix(_current_path(), $languages);
+      _current_path($path);
       if ($language !== FALSE) {
         $language_url = $language->langcode;
       }
@@ -319,7 +321,6 @@ function language_switcher_session($type, $path) {
   $links = array();
 
   $query = $_GET;
-  unset($query['q']);
 
   foreach ($languages as $language) {
     $langcode = $language->langcode;
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 4351342604e6..357166035b8c 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -197,7 +197,7 @@ function locale_init() {
   // in $conf. This should happen on all pages except the date and time formats
   // settings page, where we want to display the site default and not the
   // localized version.
-  if (strpos($_GET['q'], 'admin/config/regional/date-time/formats') !== 0) {
+  if (strpos(current_path(), 'admin/config/regional/date-time/formats') !== 0) {
     $languages = array($language_interface->langcode);
 
     // Setup appropriate date formats for this locale.
diff --git a/core/modules/locale/locale.test b/core/modules/locale/locale.test
index fa78e146860d..ded11ef68be2 100644
--- a/core/modules/locale/locale.test
+++ b/core/modules/locale/locale.test
@@ -2181,7 +2181,7 @@ class LocalePathFunctionalTest extends DrupalWebTestCase {
 
     // Test that both node titles link to our path alias.
     $this->drupalGet('<front>');
-    $custom_path_url = base_path() . (variable_get('clean_url', 0) ? $custom_path : '?q=' . $custom_path);
+    $custom_path_url = base_path() . $GLOBALS['script_path'] . $custom_path;
     $elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $first_node->title));
     $this->assertTrue(!empty($elements), t('First node links to the path alias.'));
     $elements = $this->xpath('//a[@href=:href and .=:title]', array(':href' => $custom_path_url, ':title' => $second_node->title));
@@ -2651,7 +2651,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
 
     // Check that the language switcher active link matches the given browser
     // language.
-    $args = array(':url' => base_path() . (!empty($GLOBALS['conf']['clean_url']) ? $langcode_browser_fallback : "?q=$langcode_browser_fallback"));
+    $args = array(':url' => base_path() . $GLOBALS['script_path'] . $langcode_browser_fallback);
     $fields = $this->xpath('//div[@id="block-language-language-interface"]//a[@class="language-link active" and starts-with(@href, :url)]', $args);
     $this->assertTrue($fields[0] == $languages[$langcode_browser_fallback]->name, t('The browser language is the URL active language'));
 
@@ -2686,21 +2686,21 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
     );
     $this->drupalPost('admin/config/regional/language/detection/url', $edit, t('Save configuration'));
 
-    // Build the link we're going to test based on the clean url setting.
-    $link = (!empty($GLOBALS['conf']['clean_url'])) ? 'it.example.com/admin' : 'it.example.com/?q=admin';
+    // Build the link we're going to test.
+    $link = 'it.example.com/admin';
 
     global $is_https;
-    // Test URL in another language: http://it.example.com/?q=admin.
+    // Test URL in another language: http://it.example.com/admin.
     // Base path gives problems on the testbot, so $correct_link is hard-coded.
     // @see UrlAlterFunctionalTest::assertUrlOutboundAlter (path.test).
-    $italian_url = url('admin', array('language' => $languages['it']));
+    $italian_url = url('admin', array('language' => $languages['it'], 'script' => ''));
     $url_scheme = ($is_https) ? 'https://' : 'http://';
     $correct_link = $url_scheme . $link;
     $this->assertTrue($italian_url == $correct_link, t('The url() function returns the right url (@url) in accordance with the chosen language', array('@url' => $italian_url)));
 
     // Test https via options.
     variable_set('https', TRUE);
-    $italian_url = url('admin', array('https' => TRUE, 'language' => $languages['it']));
+    $italian_url = url('admin', array('https' => TRUE, 'language' => $languages['it'], 'script' => ''));
     $correct_link = 'https://' . $link;
     $this->assertTrue($italian_url == $correct_link, t('The url() function returns the right https url (via options) (@url) in accordance with the chosen language', array('@url' => $italian_url)));
     variable_set('https', FALSE);
@@ -2708,7 +2708,7 @@ class LocaleUILanguageNegotiationTest extends DrupalWebTestCase {
     // Test https via current url scheme.
     $temp_https = $is_https;
     $is_https = TRUE;
-    $italian_url = url('admin', array('language' => $languages['it']));
+    $italian_url = url('admin', array('language' => $languages['it'], 'script' => ''));
     $correct_link = 'https://' . $link;
     $this->assertTrue($italian_url == $correct_link, t('The url() function returns the right url (via current url scheme) (@url) in accordance with the chosen language', array('@url' => $italian_url)));
     $is_https = $temp_https;
@@ -2767,9 +2767,9 @@ class LocaleUrlRewritingTest extends DrupalWebTestCase {
    * is actually not working.
    */
   private function checkUrl($language, $message1, $message2) {
-    $options = array('language' => $language);
+    $options = array('language' => $language, 'script' => '');
     $base_path = trim(base_path(), '/');
-    $rewritten_path = trim(str_replace(array('?q=', $base_path), '', url('node', $options)), '/');
+    $rewritten_path = trim(str_replace($base_path, '', url('node', $options)), '/');
     $segments = explode('/', $rewritten_path, 2);
     $prefix = $segments[0];
     $path = isset($segments[1]) ? $segments[1] : $prefix;
diff --git a/core/modules/menu/menu.admin.js b/core/modules/menu/menu.admin.js
index 4e5bf0776380..42d69d533e12 100644
--- a/core/modules/menu/menu.admin.js
+++ b/core/modules/menu/menu.admin.js
@@ -22,9 +22,8 @@ Drupal.menu_update_parent_list = function () {
     values.push(Drupal.checkPlain($.trim($(this).val())));
   });
 
-  var url = Drupal.settings.basePath + 'admin/structure/menu/parents';
   $.ajax({
-    url: location.protocol + '//' + location.host + url,
+    url: location.protocol + '//' + location.host + Drupal.url('admin/structure/menu/parents'),
     type: 'POST',
     data: {'menus[]' : values},
     dataType: 'json',
diff --git a/core/modules/openid/tests/openid_test.module b/core/modules/openid/tests/openid_test.module
index 5bd2f4ddf5bc..ac49dbd3d32a 100644
--- a/core/modules/openid/tests/openid_test.module
+++ b/core/modules/openid/tests/openid_test.module
@@ -94,19 +94,8 @@ function openid_test_yadis_xrds() {
     // Only respond to XRI requests for one specific XRI. The is used to verify
     // that the XRI has been properly encoded. The "+" sign in the _xrd_r query
     // parameter is decoded to a space by PHP.
-    if (arg(3) == 'xri') {
-      if (variable_get('clean_url', 0)) {
-        if (arg(4) != '@example*résumé;%25' || $_GET['_xrd_r'] != 'application/xrds xml') {
-          drupal_not_found();
-        }
-      }
-      else {
-        // Drupal cannot properly emulate an XRI proxy resolver using unclean
-        // URLs, so the arguments gets messed up.
-        if (arg(4) . '/' . arg(5) != '@example*résumé;%25?_xrd_r=application/xrds xml') {
-          drupal_not_found();
-        }
-      }
+    if (arg(3) == 'xri' && (arg(4) != '@example*résumé;%25' || $_GET['_xrd_r'] != 'application/xrds xml')) {
+      drupal_not_found();
     }
     drupal_add_http_header('Content-Type', 'application/xrds+xml');
     print '<?xml version="1.0" encoding="UTF-8"?>
diff --git a/core/modules/overlay/overlay-parent.js b/core/modules/overlay/overlay-parent.js
index 19d2d649a71a..aa90d9bb5069 100644
--- a/core/modules/overlay/overlay-parent.js
+++ b/core/modules/overlay/overlay-parent.js
@@ -569,7 +569,7 @@ Drupal.overlay.eventhandlerOverrideLink = function (event) {
       // If the link contains the overlay-restore class and the overlay-context
       // state is set, also update the parent window's location.
       var parentLocation = ($target.hasClass('overlay-restore') && typeof $.bbq.getState('overlay-context') == 'string')
-        ? Drupal.settings.basePath + $.bbq.getState('overlay-context')
+        ? Drupal.url($.bbq.getState('overlay-context'))
         : null;
       href = this.fragmentizeLink($target.get(0), parentLocation);
       // Only override default behavior when left-clicking and user is not
@@ -653,10 +653,10 @@ Drupal.overlay.eventhandlerOperateByURLFragment = function (event) {
   if (state) {
     // Append render variable, so the server side can choose the right
     // rendering and add child frame code to the page if needed.
-    var url = $.param.querystring(Drupal.settings.basePath + state, { render: 'overlay' });
+    var url = $.param.querystring(Drupal.url(state), { render: 'overlay' });
 
     this.open(url);
-    this.resetActiveClass(this.getPath(Drupal.settings.basePath + state));
+    this.resetActiveClass(this.getPath(url));
   }
   // If there is no overlay URL in the fragment and the overlay is (still)
   // open, close the overlay.
@@ -688,7 +688,7 @@ Drupal.overlay.eventhandlerSyncURLFragment = function (event) {
   if (this.isOpen) {
     var expected = $.bbq.getState('overlay');
     // This is just a sanity check, so we're comparing paths, not query strings.
-    if (this.getPath(Drupal.settings.basePath + expected) != this.getPath(this.iframeWindow.document.location)) {
+    if (this.getPath(Drupal.url(expected)) != this.getPath(this.iframeWindow.document.location)) {
       // There may have been a redirect inside the child overlay window that the
       // parent wasn't aware of. Update the parent URL fragment appropriately.
       var newLocation = Drupal.overlay.fragmentizeLink(this.iframeWindow.document.location);
@@ -752,10 +752,8 @@ Drupal.overlay.fragmentizeLink = function (link, parentLocation) {
     return link.href;
   }
 
-  // Determine the link's original destination. Set ignorePathFromQueryString to
-  // true to prevent transforming this link into a clean URL while clean URLs
-  // may be disabled.
-  var path = this.getPath(link, true);
+  // Determine the link's original destination.
+  var path = this.getPath(link);
   // Preserve existing query and fragment parameters in the URL, except for
   // "render=overlay" which is re-added in Drupal.overlay.eventhandlerOperateByURLFragment.
   var destination = path + link.search.replace(/&?render=overlay/, '').replace(/\?$/, '') + link.hash;
@@ -783,7 +781,7 @@ Drupal.overlay.refreshRegions = function (data) {
           (function (regionName, regionSelector) {
             var $region = $(regionSelector);
             Drupal.detachBehaviors($region);
-            $.get(Drupal.settings.basePath + Drupal.settings.overlay.ajaxCallback + '/' + regionName, function (newElement) {
+            $.get(Drupal.url(Drupal.settings.overlay.ajaxCallback + '/' + regionName), function (newElement) {
               $region.replaceWith($(newElement));
               Drupal.attachBehaviors($region, Drupal.settings);
             });
@@ -827,13 +825,11 @@ Drupal.overlay.resetActiveClass = function(activePath) {
  *
  * @param link
  *   Link object or string to get the Drupal path from.
- * @param ignorePathFromQueryString
- *   Boolean whether to ignore path from query string if path appears empty.
  *
  * @return
  *   The Drupal path.
  */
-Drupal.overlay.getPath = function (link, ignorePathFromQueryString) {
+Drupal.overlay.getPath = function (link) {
   if (typeof link == 'string') {
     // Create a native Link object, so we can use its object methods.
     link = $(link.link(link)).get(0);
@@ -844,15 +840,7 @@ Drupal.overlay.getPath = function (link, ignorePathFromQueryString) {
   if (path.charAt(0) != '/') {
     path = '/' + path;
   }
-  path = path.replace(new RegExp(Drupal.settings.basePath + '(?:index.php)?'), '');
-  if (path == '' && !ignorePathFromQueryString) {
-    // If the path appears empty, it might mean the path is represented in the
-    // query string (clean URLs are not used).
-    var match = new RegExp('([?&])q=(.+)([&#]|$)').exec(link.search);
-    if (match && match.length == 4) {
-      path = match[2];
-    }
-  }
+  path = path.replace(new RegExp(Drupal.settings.basePath + Drupal.settings.scriptPath), '');
 
   return path;
 };
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index e2ecd19144a8..271f15dd5a97 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -143,7 +143,7 @@ function overlay_init() {
       // If this page shouldn't be rendered inside the overlay, redirect to the
       // parent.
       elseif (!path_is_admin($current_path)) {
-        overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('q', 'render'))));
+        overlay_close_dialog($current_path, array('query' => drupal_get_query_parameters(NULL, array('render'))));
       }
 
       // Indicate that we are viewing an overlay child page.
diff --git a/core/modules/path/path.admin.inc b/core/modules/path/path.admin.inc
index 9e22cffd3187..d45ba59a4fc5 100644
--- a/core/modules/path/path.admin.inc
+++ b/core/modules/path/path.admin.inc
@@ -129,7 +129,7 @@ function path_admin_form($form, &$form_state, $path = array('source' => '', 'ali
     '#maxlength' => 255,
     '#size' => 45,
     '#description' => t('Specify the existing path you wish to alias. For example: node/28, forum/1, taxonomy/term/1.'),
-    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
+    '#field_prefix' => url(NULL, array('absolute' => TRUE)),
     '#required' => TRUE,
   );
   $form['alias'] = array(
@@ -139,7 +139,7 @@ function path_admin_form($form, &$form_state, $path = array('source' => '', 'ali
     '#maxlength' => 255,
     '#size' => 45,
     '#description' => t('Specify an alternative path by which this data can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
-    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
+    '#field_prefix' => url(NULL, array('absolute' => TRUE)),
     '#required' => TRUE,
   );
 
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 9a6a11adac0f..4e761505557f 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -239,7 +239,7 @@ function search_menu() {
 }
 
 /**
- * Determines access for the ?q=search path.
+ * Determines access for the 'search' path.
  */
 function search_is_active() {
   // This path cannot be accessed if there are no active modules.
diff --git a/core/modules/shortcut/shortcut.admin.inc b/core/modules/shortcut/shortcut.admin.inc
index 75c12b404d69..9010f9063229 100644
--- a/core/modules/shortcut/shortcut.admin.inc
+++ b/core/modules/shortcut/shortcut.admin.inc
@@ -489,7 +489,7 @@ function _shortcut_link_form_elements($shortcut_link = NULL) {
     '#title' => t('Path'),
     '#size' => 40,
     '#maxlength' => 255,
-    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
+    '#field_prefix' => url(NULL, array('absolute' => TRUE)),
     '#default_value' => $shortcut_link['link_path'],
   );
 
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index aa43503da346..73b63850eaee 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -657,7 +657,7 @@ function shortcut_preprocess_page(&$variables) {
   // we do not want to display it on "access denied" or "page not found"
   // pages).
   if (shortcut_set_edit_access() && ($item = menu_get_item()) && $item['access']) {
-    $link = $_GET['q'];
+    $link = current_path();
     $query_parameters = drupal_get_query_parameters();
     if (!empty($query_parameters)) {
      $link .= '?' . drupal_http_build_query($query_parameters);
diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php
index 540dc0e9385a..4a01cdef9942 100644
--- a/core/modules/simpletest/drupal_web_test_case.php
+++ b/core/modules/simpletest/drupal_web_test_case.php
@@ -1318,7 +1318,6 @@ protected function setUp() {
     $this->originalConfigSignatureKey = $GLOBALS['config_signature_key'];
     $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
     $this->originalProfile = drupal_get_profile();
-    $clean_url_original = variable_get('clean_url', 0);
 
     // Set to English to prevent exceptions from utf8_truncate() from t()
     // during install if the current language is not 'en'.
@@ -1435,7 +1434,6 @@ protected function setUp() {
 
     // Restore necessary variables.
     variable_set('install_task', 'done');
-    variable_set('clean_url', $clean_url_original);
     variable_set('site_mail', 'simpletest@example.com');
     variable_set('date_default_timezone', date_default_timezone_get());
     // Set up English language.
diff --git a/core/modules/simpletest/simpletest.test b/core/modules/simpletest/simpletest.test
index 4cecbe6b8afd..185a71ab55cf 100644
--- a/core/modules/simpletest/simpletest.test
+++ b/core/modules/simpletest/simpletest.test
@@ -341,8 +341,6 @@ class SimpleTestBrowserTestCase extends DrupalWebTestCase {
    * Test DrupalWebTestCase::getAbsoluteUrl().
    */
   function testGetAbsoluteUrl() {
-    // Testbed runs with Clean URLs disabled, so disable it here.
-    variable_set('clean_url', 0);
     $url = 'user/login';
 
     $this->drupalGet($url);
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index 69d5f5be38fc..654621dbc453 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -67,7 +67,9 @@ function statistics_exit() {
     db_insert('accesslog')
       ->fields(array(
         'title' => truncate_utf8(strip_tags(drupal_get_title()), 255),
-        'path' => truncate_utf8($_GET['q'], 255),
+        // @todo The public function current_path() is not available on a cache
+        //   hit.
+        'path' => truncate_utf8(_current_path(), 255),
         'url' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
         'hostname' => ip_address(),
         'uid' => $user->uid,
diff --git a/core/modules/statistics/statistics.test b/core/modules/statistics/statistics.test
index 6f19e37f8bfe..331476174b1d 100644
--- a/core/modules/statistics/statistics.test
+++ b/core/modules/statistics/statistics.test
@@ -152,8 +152,13 @@ class StatisticsLoggingTestCase extends DrupalWebTestCase {
     $this->assertTrue(is_array($log) && count($log) == 7, t('Page request was logged.'));
     $this->assertEqual(array_intersect_key($log[6], $expected), $expected);
 
-    // Create a path longer than 255 characters.
-    $long_path = $this->randomName(256);
+    // Create a path longer than 255 characters. Drupal's .htaccess file
+    // instructs Apache to test paths against the file system before routing to
+    // index.php. Many file systems restrict file names to 255 characters
+    // (http://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits), and
+    // Apache returns a 403 when testing longer file names, but the total path
+    // length is not restricted.
+    $long_path = $this->randomName(127) . '/' . $this->randomName(128);
 
     // Test that the long path is properly truncated when logged.
     $this->drupalGet($long_path);
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index aac0c3dded21..9619461d3033 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1494,7 +1494,7 @@ function system_site_information_settings() {
     '#default_value' => (variable_get('site_frontpage') != 'user' ? drupal_get_path_alias(variable_get('site_frontpage', 'user')) : ''),
     '#size' => 40,
     '#description' => t('Optionally, specify a relative URL to display as the front page.  Leave blank to display the default content feed.'),
-    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q='),
+    '#field_prefix' => url(NULL, array('absolute' => TRUE)),
   );
   $form['front_page']['default_nodes_main'] = array(
     '#type' => 'select', '#title' => t('Number of posts on front page'),
@@ -1513,7 +1513,7 @@ function system_site_information_settings() {
     '#default_value' => variable_get('site_403', ''),
     '#size' => 40,
     '#description' => t('This page is displayed when the requested document is denied to the current user. Leave blank to display a generic "access denied" page.'),
-    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
+    '#field_prefix' => url(NULL, array('absolute' => TRUE)),
   );
   $form['error_page']['site_404'] = array(
     '#type' => 'textfield',
@@ -1521,7 +1521,7 @@ function system_site_information_settings() {
     '#default_value' => variable_get('site_404', ''),
     '#size' => 40,
     '#description' => t('This page is displayed when no other content matches the requested document. Leave blank to display a generic "page not found" page.'),
-    '#field_prefix' => url(NULL, array('absolute' => TRUE)) . (variable_get('clean_url', 0) ? '' : '?q=')
+    '#field_prefix' => url(NULL, array('absolute' => TRUE)),
   );
 
   $form['#validate'][] = 'system_site_information_settings_validate';
@@ -2205,96 +2205,6 @@ function system_site_maintenance_mode() {
   return system_settings_form($form);
 }
 
-/**
- * Form builder; Configure clean URL settings.
- *
- * @ingroup forms
- * @see system_settings_form()
- */
-function system_clean_url_settings($form, &$form_state) {
-  $available = FALSE;
-  $conflict = FALSE;
-
-  // If the request URI is a clean URL, clean URLs must be available.
-  // Otherwise, run a test.
-  if (strpos(request_uri(), '?q=') === FALSE && strpos(request_uri(), '&q=') === FALSE) {
-    $available = TRUE;
-  }
-  else {
-    $request = drupal_http_request($GLOBALS['base_url'] . '/admin/config/search/clean-urls/check');
-    // If the request returns HTTP 200, clean URLs are available.
-    if (isset($request->code) && $request->code == 200) {
-      $available = TRUE;
-      // If the user started the clean URL test, provide explicit feedback.
-      if (isset($form_state['input']['clean_url_test_execute'])) {
-        drupal_set_message(t('The clean URL test passed.'));
-      }
-    }
-    else {
-      // If the test failed while clean URLs are enabled, make sure clean URLs
-      // can be disabled.
-      if (variable_get('clean_url', 0)) {
-        $conflict = TRUE;
-        // Warn the user of a conflicting situation, unless after processing
-        // a submitted form.
-        if (!isset($form_state['input']['op'])) {
-          drupal_set_message(t('Clean URLs are enabled, but the clean URL test failed. Uncheck the box below to disable clean URLs.'), 'warning');
-        }
-      }
-      // If the user started the clean URL test, provide explicit feedback.
-      elseif (isset($form_state['input']['clean_url_test_execute'])) {
-        drupal_set_message(t('The clean URL test failed.'), 'warning');
-      }
-    }
-  }
-
-  // Show the enable/disable form if clean URLs are available or if the user
-  // must be able to resolve a conflicting setting.
-  if ($available || $conflict) {
-    $form['clean_url'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Enable clean URLs'),
-      '#default_value' => variable_get('clean_url', 0),
-      '#description' => t('Use URLs like <code>example.com/user</code> instead of <code>example.com/?q=user</code>.'),
-    );
-    $form = system_settings_form($form);
-    if ($conflict) {
-      // $form_state['redirect'] needs to be set to the non-clean URL,
-      // otherwise the setting is not saved.
-      $form_state['redirect'] = url('', array('query' => array('q' => '/admin/config/search/clean-urls')));
-    }
-  }
-  // Show the clean URLs test form.
-  else {
-    drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
-
-    $form_state['redirect'] = url('admin/config/search/clean-urls');
-    $form['clean_url_description'] = array(
-      '#type' => 'markup',
-      '#markup' => '<p>' . t('Use URLs like <code>example.com/user</code> instead of <code>example.com/?q=user</code>.'),
-    );
-    // Explain why the user is seeing this page and what to expect after
-    // clicking the 'Run the clean URL test' button.
-    $form['clean_url_test_result'] = array(
-      '#type' => 'markup',
-      '#markup' => '<p>' . t('Clean URLs cannot be enabled. If you are directed to this page or to a <em>Page not found (404)</em> error after testing for clean URLs, see the <a href="@handbook">online handbook</a>.', array('@handbook' => 'http://drupal.org/node/15365')) . '</p>',
-    );
-    $form['actions'] = array(
-      '#type' => 'actions',
-      'clean_url_test' => array(
-        '#type' => 'submit',
-        '#value' => t('Run the clean URL test'),
-      ),
-    );
-    $form['clean_url_test_execute'] = array(
-      '#type' => 'hidden',
-      '#value' => 1,
-    );
-  }
-
-  return $form;
-}
-
 /**
  * Menu callback: displays the site status report. Can also be used as a pure check.
  *
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index c6e1288b9be4..15ffa2175695 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -497,7 +497,7 @@ function hook_page_build(&$page) {
  */
 function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
   // When retrieving the router item for the current path...
-  if ($path == $_GET['q']) {
+  if ($path == current_path()) {
     // ...call a function that prepares something for this request.
     mymodule_prepare_something();
   }
diff --git a/core/modules/system/system.cron.js b/core/modules/system/system.cron.js
index af17dab52739..aa5b35113860 100644
--- a/core/modules/system/system.cron.js
+++ b/core/modules/system/system.cron.js
@@ -9,7 +9,7 @@ Drupal.behaviors.cronCheck = {
       $('body').once('cron-check', function() {
         // Only execute the cron check if its the right time.
         if (Math.round(new Date().getTime() / 1000.0) > settings.cronCheck) {
-          $.get(settings.basePath + 'system/run-cron-check');
+          $.get(Drupal.url('system/run-cron-check'));
         }
       });
     }
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 51603ffe3c8d..f0e36cc3ceff 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1848,6 +1848,13 @@ function system_update_8007() {
   }
 }
 
+/**
+ * Remove the 'clean_url' configuration variable.
+ */
+function system_update_8008() {
+  variable_del('clean_url');
+}
+
 /**
  * @} End of "defgroup updates-7.x-to-8.x"
  * The next series of updates should start at 9000.
diff --git a/core/modules/system/system.js b/core/modules/system/system.js
index 98da1a660767..ab00426b2024 100644
--- a/core/modules/system/system.js
+++ b/core/modules/system/system.js
@@ -19,55 +19,6 @@ Drupal.hideEmailAdministratorCheckbox = function () {
   });
 };
 
-/**
- * Internal function to check using Ajax if clean URLs can be enabled on the
- * settings page.
- *
- * This function is not used to verify whether or not clean URLs
- * are currently enabled.
- */
-Drupal.behaviors.cleanURLsSettingsCheck = {
-  attach: function (context, settings) {
-    // This behavior attaches by ID, so is only valid once on a page.
-    // Also skip if we are on an install page, as Drupal.cleanURLsInstallCheck will handle
-    // the processing.
-    if (!($('#edit-clean-url').length) || $('#edit-clean-url.install').once('clean-url').length) {
-      return;
-    }
-    var url = settings.basePath + 'admin/config/search/clean-urls/check';
-    $.ajax({
-      url: location.protocol + '//' + location.host + url,
-      dataType: 'json',
-      success: function () {
-        // Check was successful. Redirect using a "clean URL". This will force the form that allows enabling clean URLs.
-        location = settings.basePath +"admin/config/search/clean-urls";
-      }
-    });
-  }
-};
-
-/**
- * Internal function to check using Ajax if clean URLs can be enabled on the
- * install page.
- *
- * This function is not used to verify whether or not clean URLs
- * are currently enabled.
- */
-Drupal.cleanURLsInstallCheck = function () {
-  var url = location.protocol + '//' + location.host + Drupal.settings.basePath + 'admin/config/search/clean-urls/check';
-  // Submit a synchronous request to avoid database errors associated with
-  // concurrent requests during install.
-  $.ajax({
-    async: false,
-    url: url,
-    dataType: 'json',
-    success: function () {
-      // Check was successful.
-      $('#edit-clean-url').attr('value', 1);
-    }
-  });
-};
-
 /**
  * When a field is filled out, apply its value to other fields that will likely
  * use the same value. In the installer this is used to populate the
diff --git a/core/modules/system/system.maintenance.css b/core/modules/system/system.maintenance.css
index 5543c2db816e..c6a5a4991510 100644
--- a/core/modules/system/system.maintenance.css
+++ b/core/modules/system/system.maintenance.css
@@ -46,10 +46,3 @@
 ol.task-list li.active {
   font-weight: bold;
 }
-
-/**
- * Installation clean URLs
- */
-#clean-url.install {
-  display: none;
-}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index dfb8936cd479..20afc296d6b4 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -88,7 +88,7 @@ function system_help($path, $arg) {
       $output .= '<dt>' . t('Performing system maintenance') . '</dt>';
       $output .= '<dd>' . t('In order for the site and its modules to continue to operate well, a set of routine administrative operations must run on a regular basis. The System module manages this task by making use of a system cron job. You can verify the status of cron tasks by visiting the <a href="@status">Status report page</a>. For more information, see the online handbook entry for <a href="@handbook">configuring cron jobs</a>. You can set up cron job by visiting <a href="@cron">Cron configuration</a> page', array('@status' => url('admin/reports/status'), '@handbook' => 'http://drupal.org/cron', '@cron' => url('admin/config/system/cron'))) . '</dd>';
       $output .= '<dt>' . t('Configuring basic site settings') . '</dt>';
-      $output .= '<dd>' . t('The System module also handles basic configuration options for your site, including <a href="@date-time-settings">Date and time settings</a>, <a href="@file-system">File system settings</a>, <a href="@clean-url">Clean URL support</a>, <a href="@site-info">Site name and other information</a>, and a <a href="@maintenance-mode">Maintenance mode</a> for taking your site temporarily offline.', array('@date-time-settings' => url('admin/config/regional/date-time'), '@file-system' => url('admin/config/media/file-system'), '@clean-url' => url('admin/config/search/clean-urls'), '@site-info' => url('admin/config/system/site-information'), '@maintenance-mode' => url('admin/config/development/maintenance'))) . '</dd>';
+      $output .= '<dd>' . t('The System module also handles basic configuration options for your site, including <a href="@date-time-settings">Date and time settings</a>, <a href="@file-system">File system settings</a>, <a href="@site-info">Site name and other information</a>, and a <a href="@maintenance-mode">Maintenance mode</a> for taking your site temporarily offline.', array('@date-time-settings' => url('admin/config/regional/date-time'), '@file-system' => url('admin/config/media/file-system'), '@site-info' => url('admin/config/system/site-information'), '@maintenance-mode' => url('admin/config/development/maintenance'))) . '</dd>';
       $output .= '<dt>' . t('Configuring actions') . '</dt>';
       $output .= '<dd>' . t('Actions are individual tasks that the system can do, such as unpublishing a piece of content or banning a user. Other modules can fire these actions when certain system events happen; for example, when a new post is added or when a user logs in. Modules may also provide additional actions. Visit the <a href="@actions">Actions page</a> to configure actions.', array('@actions' => url('admin/config/system/actions'))) . '</dd>';
       $output .= '</dl>';
@@ -971,23 +971,6 @@ function system_menu() {
     'access arguments' => array('access administration pages'),
     'file' => 'system.admin.inc',
   );
-  $items['admin/config/search/clean-urls'] = array(
-    'title' => 'Clean URLs',
-    'description' => 'Enable or disable clean URLs for your site.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('system_clean_url_settings'),
-    'access arguments' => array('administer site configuration'),
-    'file' => 'system.admin.inc',
-    'weight' => 5,
-  );
-  $items['admin/config/search/clean-urls/check'] = array(
-    'title' => 'Clean URL check',
-    'page callback' => 'drupal_json_output',
-    'page arguments' => array(array('status' => TRUE)),
-    'access callback' => TRUE,
-    'type' => MENU_CALLBACK,
-    'file' => 'system.admin.inc',
-  );
 
   // System settings.
   $items['admin/config/system'] = array(
@@ -1850,8 +1833,7 @@ function system_authorized_get_url(array $options = array()) {
   global $base_url;
   // Force https if available, regardless of what the caller specifies.
   $options['https'] = TRUE;
-  // We prefix with $base_url so we get a full path even if clean URLs are
-  // disabled.
+  // Prefix with $base_url so url() treats it as an external link.
   return url($base_url . '/core/authorize.php', $options);
 }
 
diff --git a/core/modules/system/system.test b/core/modules/system/system.test
index 16348cc8203c..4fdad7172e1d 100644
--- a/core/modules/system/system.test
+++ b/core/modules/system/system.test
@@ -2896,10 +2896,7 @@ class SystemIndexPhpTest extends DrupalWebTestCase {
     $this->drupalGet($index_php, array('external' => TRUE));
     $this->assertResponse(200, t('Make sure index.php returns a valid page.'));
 
-    $this->drupalGet($index_php, array('external' => TRUE, 'query' => array('q' => 'user')));
-    $this->assertResponse(200, t('Make sure index.php?q=user returns a valid page.'));
-
     $this->drupalGet($index_php .'/user', array('external' => TRUE));
-    $this->assertResponse(404, t("Make sure index.php/user returns a 'page not found'."));
+    $this->assertResponse(200, t("Make sure index.php/user returns a valid page."));
   }
 }
diff --git a/core/modules/system/tests/ajax.test b/core/modules/system/tests/ajax.test
index 4f254f81f382..49f1632981bf 100644
--- a/core/modules/system/tests/ajax.test
+++ b/core/modules/system/tests/ajax.test
@@ -80,7 +80,7 @@ class AJAXFrameworkTestCase extends AJAXTestCase {
     // drupal_add_js().
     $expected = array(
       'command' => 'settings',
-      'settings' => array('basePath' => base_path(), 'ajax' => 'test'),
+      'settings' => array('ajax' => 'test'),
     );
     $this->assertCommand($commands, $expected, t('ajax_render() loads settings added with drupal_add_js().'));
 
diff --git a/core/modules/system/tests/common.test b/core/modules/system/tests/common.test
index f0ac957ef22b..46b379ee799a 100644
--- a/core/modules/system/tests/common.test
+++ b/core/modules/system/tests/common.test
@@ -97,7 +97,7 @@ class CommonURLUnitTestCase extends DrupalWebTestCase {
    * Tests for active class in l() function.
    */
   function testLActiveClass() {
-    $link = l($this->randomName(), $_GET['q']);
+    $link = l($this->randomName(), current_path());
     $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
   }
 
@@ -106,7 +106,7 @@ class CommonURLUnitTestCase extends DrupalWebTestCase {
    */
   function testLCustomClass() {
     $class = $this->randomName();
-    $link = l($this->randomName(), $_GET['q'], array('attributes' => array('class' => array($class))));
+    $link = l($this->randomName(), current_path(), array('attributes' => array('class' => array($class))));
     $this->assertTrue($this->hasClass($link, $class), t('Custom class @class is present on link when requested', array('@class' => $class)));
     $this->assertTrue($this->hasClass($link, 'active'), t('Class @class is present on link to the current page', array('@class' => 'active')));
   }
@@ -128,19 +128,8 @@ class CommonURLUnitTestCase extends DrupalWebTestCase {
         ),
       ),
       'c' => 3,
-      'q' => 'foo/bar',
     );
 
-    // Default arguments.
-    $result = $_GET;
-    unset($result['q']);
-    $this->assertEqual(drupal_get_query_parameters(), $result, t("\$_GET['q'] was removed."));
-
-    // Default exclusion.
-    $result = $original;
-    unset($result['q']);
-    $this->assertEqual(drupal_get_query_parameters($original), $result, t("'q' was removed."));
-
     // First-level exclusion.
     $result = $original;
     unset($result['b']);
@@ -176,14 +165,21 @@ class CommonURLUnitTestCase extends DrupalWebTestCase {
    * Test drupal_parse_url().
    */
   function testDrupalParseUrl() {
-    // Relative URL.
-    $url = 'foo/bar?foo=bar&bar=baz&baz#foo';
-    $result = array(
-      'path' => 'foo/bar',
-      'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
-      'fragment' => 'foo',
-    );
-    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
+    // Relative, absolute, and external URLs, without/with explicit script path,
+    // without/with Drupal path.
+    foreach (array('', '/', 'http://drupal.org/') as $absolute) {
+      foreach (array('', 'index.php/') as $script) {
+        foreach (array('', 'foo/bar') as $path) {
+          $url = $absolute . $script . $path . '?foo=bar&bar=baz&baz#foo';
+          $expected = array(
+            'path' => $absolute . $script . $path,
+            'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
+            'fragment' => 'foo',
+          );
+          $this->assertEqual(drupal_parse_url($url), $expected, t('URL parsed correctly.'));
+        }
+      }
+    }
 
     // Relative URL that is known to confuse parse_url().
     $url = 'foo/bar:1';
@@ -194,47 +190,10 @@ class CommonURLUnitTestCase extends DrupalWebTestCase {
     );
     $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL parsed correctly.'));
 
-    // Absolute URL.
-    $url = '/foo/bar?foo=bar&bar=baz&baz#foo';
-    $result = array(
-      'path' => '/foo/bar',
-      'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
-      'fragment' => 'foo',
-    );
-    $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL parsed correctly.'));
-
-    // External URL testing.
-    $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
-
     // Test that drupal can recognize an absolute URL. Used to prevent attack vectors.
+    $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
     $this->assertTrue(url_is_external($url), t('Correctly identified an external URL.'));
 
-    // Test the parsing of absolute URLs.
-    $result = array(
-      'path' => 'http://drupal.org/foo/bar',
-      'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
-      'fragment' => 'foo',
-    );
-    $this->assertEqual(drupal_parse_url($url), $result, t('External URL parsed correctly.'));
-
-    // Verify proper parsing of URLs when clean URLs are disabled.
-    $result = array(
-      'path' => 'foo/bar',
-      'query' => array('bar' => 'baz'),
-      'fragment' => 'foo',
-    );
-    // Non-clean URLs #1: Absolute URL generated by url().
-    $url = $GLOBALS['base_url'] . '/?q=foo/bar&bar=baz#foo';
-    $this->assertEqual(drupal_parse_url($url), $result, t('Absolute URL with clean URLs disabled parsed correctly.'));
-
-    // Non-clean URLs #2: Relative URL generated by url().
-    $url = '?q=foo/bar&bar=baz#foo';
-    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL with clean URLs disabled parsed correctly.'));
-
-    // Non-clean URLs #3: URL generated by url() on non-Apache webserver.
-    $url = 'index.php?q=foo/bar&bar=baz#foo';
-    $this->assertEqual(drupal_parse_url($url), $result, t('Relative URL on non-Apache webserver with clean URLs disabled parsed correctly.'));
-
     // Test that drupal_parse_url() does not allow spoofing a URL to force a malicious redirect.
     $parts = drupal_parse_url('forged:http://cwe.mitre.org/data/definitions/601.html');
     $this->assertFalse(valid_url($parts['path'], TRUE), t('drupal_parse_url() correctly parsed a forged URL.'));
@@ -245,75 +204,41 @@ class CommonURLUnitTestCase extends DrupalWebTestCase {
    * assert all that works when clean URLs are on and off.
    */
   function testUrl() {
-    global $base_url;
-
-    foreach (array(FALSE, TRUE) as $absolute) {
-      // Get the expected start of the path string.
-      $base = $absolute ? $base_url . '/' : base_path();
-      $absolute_string = $absolute ? 'absolute' : NULL;
-
-      // Disable Clean URLs.
-      $GLOBALS['conf']['clean_url'] = 0;
-
-      $url = $base . '?q=node/123';
-      $result = url('node/123', array('absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123#foo';
-      $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123&foo';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123&foo=bar&bar=baz';
-      $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123&foo#bar';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123&foo#0';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '0', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . '?q=node/123&foo';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base;
-      $result = url('<front>', array('absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      // Enable Clean URLs.
-      $GLOBALS['conf']['clean_url'] = 1;
-
-      $url = $base . 'node/123';
-      $result = url('node/123', array('absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . 'node/123#foo';
-      $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . 'node/123?foo';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . 'node/123?foo=bar&bar=baz';
-      $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base . 'node/123?foo#bar';
-      $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
-
-      $url = $base;
-      $result = url('<front>', array('absolute' => $absolute));
-      $this->assertEqual($url, $result, "$url == $result");
+    global $base_url, $script_path;
+
+    $script_path_original = $script_path;
+    foreach (array('', 'index.php/') as $script_path) {
+      foreach (array(FALSE, TRUE) as $absolute) {
+        // Get the expected start of the path string.
+        $base = ($absolute ? $base_url . '/' : base_path()) . $script_path;
+        $absolute_string = $absolute ? 'absolute' : NULL;
+
+        $url = $base . 'node/123';
+        $result = url('node/123', array('absolute' => $absolute));
+        $this->assertEqual($url, $result, "$url == $result");
+
+        $url = $base . 'node/123#foo';
+        $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
+        $this->assertEqual($url, $result, "$url == $result");
+
+        $url = $base . 'node/123?foo';
+        $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
+        $this->assertEqual($url, $result, "$url == $result");
+
+        $url = $base . 'node/123?foo=bar&bar=baz';
+        $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
+        $this->assertEqual($url, $result, "$url == $result");
+
+        $url = $base . 'node/123?foo#bar';
+        $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
+        $this->assertEqual($url, $result, "$url == $result");
+
+        $url = $base;
+        $result = url('<front>', array('absolute' => $absolute));
+        $this->assertEqual($url, $result, "$url == $result");
+      }
     }
+    $script_path = $script_path_original;
   }
 
   /**
@@ -1230,8 +1155,8 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
     $this->assertTrue(array_key_exists('core/misc/html5.js', $javascript), t('html5.js is added when file is added.'));
     $this->assertTrue(array_key_exists('core/misc/collapse.js', $javascript), t('JavaScript files are correctly added.'));
     $this->assertEqual(base_path(), $javascript['settings']['data'][0]['basePath'], t('Base path JavaScript setting is correctly set.'));
-    url('', array('prefix' => &$prefix));
-    $this->assertEqual(empty($prefix) ? '' : $prefix, $javascript['settings']['data'][1]['pathPrefix'], t('Path prefix JavaScript setting is correctly set.'));
+    $this->assertIdentical($GLOBALS['script_path'], $javascript['settings']['data'][1]['scriptPath'], t('Script path JavaScript setting is correctly set.'));
+    $this->assertIdentical('', $javascript['settings']['data'][2]['pathPrefix'], t('Path prefix JavaScript setting is correctly set.'));
   }
 
   /**
@@ -1239,8 +1164,8 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
    */
   function testAddSetting() {
     $javascript = drupal_add_js(array('drupal' => 'rocks', 'dries' => 280342800), 'setting');
-    $this->assertEqual(280342800, $javascript['settings']['data'][2]['dries'], t('JavaScript setting is set correctly.'));
-    $this->assertEqual('rocks', $javascript['settings']['data'][2]['drupal'], t('The other JavaScript setting is set correctly.'));
+    $this->assertEqual(280342800, $javascript['settings']['data'][3]['dries'], t('JavaScript setting is set correctly.'));
+    $this->assertEqual('rocks', $javascript['settings']['data'][3]['drupal'], t('The other JavaScript setting is set correctly.'));
   }
 
   /**
@@ -1269,8 +1194,9 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
 
     $javascript = drupal_get_js('header');
     $this->assertTrue(strpos($javascript, 'basePath') > 0, t('Rendered JavaScript header returns basePath setting.'));
-    $this->assertTrue(strpos($javascript, 'core/misc/jquery.js') > 0, t('Rendered JavaScript header includes jQuery.'));
+    $this->assertTrue(strpos($javascript, 'scriptPath') > 0, t('Rendered JavaScript header returns scriptPath setting.'));
     $this->assertTrue(strpos($javascript, 'pathPrefix') > 0, t('Rendered JavaScript header returns pathPrefix setting.'));
+    $this->assertTrue(strpos($javascript, 'core/misc/jquery.js') > 0, t('Rendered JavaScript header includes jQuery.'));
 
     // Test whether drupal_add_js can be used to override a previous setting.
     $this->assertTrue(strpos($javascript, 'commonTestShouldAppear') > 0, t('Rendered JavaScript header returns custom setting.'));
@@ -1974,8 +1900,8 @@ class CommonValidUrlUnitTestCase extends DrupalUnitTestCase {
       'example.com/index.html#pagetop',
       'example.com:8080',
       'subdomain.example.com',
-      'example.com/index.php?q=node',
-      'example.com/index.php?q=node&param=false',
+      'example.com/index.php/node',
+      'example.com/index.php/node?param=false',
       'user@www.example.com',
       'user:pass@www.example.com:8080/login.php?do=login&style=%23#pagetop',
       '127.0.0.1',
@@ -2022,8 +1948,8 @@ class CommonValidUrlUnitTestCase extends DrupalUnitTestCase {
     $valid_relative_urls = array(
       'paren(the)sis',
       'index.html#pagetop',
-      'index.php?q=node',
-      'index.php?q=node&param=false',
+      'index.php/node',
+      'index.php/node?param=false',
       'login.php?do=login&style=%23#pagetop',
     );
 
diff --git a/core/modules/system/tests/file.test b/core/modules/system/tests/file.test
index 173d05abb743..ab930090fee2 100644
--- a/core/modules/system/tests/file.test
+++ b/core/modules/system/tests/file.test
@@ -2422,7 +2422,7 @@ class FileDownloadTest extends FileTestCase {
    * Test file_create_url().
    */
   function testFileCreateUrl() {
-    global $base_url;
+    global $base_url, $script_path;
 
     // Tilde (~) is excluded from this test because it is encoded by
     // rawurlencode() in PHP 5.2 but not in PHP 5.3, as per RFC 3986.
@@ -2434,9 +2434,16 @@ class FileDownloadTest extends FileTestCase {
       '%2523%2525%2526%252B%252F%253F' .
       '%C3%A9%C3%B8%C3%AF%D0%B2%CE%B2%E4%B8%AD%E5%9C%8B%E6%9B%B8%DB%9E';
 
-    $this->checkUrl('public', '', $basename, $base_url . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded);
-    $this->checkUrl('private', '', $basename, $base_url . '/system/files/' . $basename_encoded);
-    $this->checkUrl('private', '', $basename, $base_url . '/?q=system/files/' . $basename_encoded, '0');
+    // Public files should not be served by Drupal, so their URLs should not be
+    // generated by url(), whereas private files should be served by Drupal, so
+    // their URLs should be generated by url(). The difference is most apparent
+    // when $script_path is not empty (i.e., when not using clean URLs).
+    $script_path_original = $script_path;
+    foreach (array('', 'index.php/') as $script_path) {
+      $this->checkUrl('public', '', $basename, $base_url . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded);
+      $this->checkUrl('private', '', $basename, $base_url . '/' . $script_path . 'system/files/' . $basename_encoded);
+    }
+    $script_path = $script_path_original;
   }
 
   /**
@@ -2454,12 +2461,8 @@ class FileDownloadTest extends FileTestCase {
    *   A filename
    * @param $expected_url
    *   The expected URL
-   * @param $clean_url
-   *   The value of the clean_url setting
    */
-  private function checkUrl($scheme, $directory, $filename, $expected_url, $clean_url = '1') {
-    variable_set('clean_url', $clean_url);
-
+  private function checkUrl($scheme, $directory, $filename, $expected_url) {
     // Convert $filename to a valid filename, i.e. strip characters not
     // supported by the filesystem, and create the file in the specified
     // directory.
diff --git a/core/modules/system/tests/menu.test b/core/modules/system/tests/menu.test
index 0a10ec8c4fd8..c17adb57620c 100644
--- a/core/modules/system/tests/menu.test
+++ b/core/modules/system/tests/menu.test
@@ -262,11 +262,11 @@ class MenuRouterTestCase extends DrupalWebTestCase {
 
     $this->drupalGet('user/login');
     // Check that we got to 'user'.
-    $this->assertTrue($this->url == url('user', array('absolute' => TRUE)), t("Logged-in user redirected to q=user on accessing q=user/login"));
+    $this->assertTrue($this->url == url('user', array('absolute' => TRUE)), t("Logged-in user redirected to user on accessing user/login"));
 
     // user/register should redirect to user/UID/edit.
     $this->drupalGet('user/register');
-    $this->assertTrue($this->url == url('user/' . $this->loggedInUser->uid . '/edit', array('absolute' => TRUE)), t("Logged-in user redirected to q=user/UID/edit on accessing q=user/register"));
+    $this->assertTrue($this->url == url('user/' . $this->loggedInUser->uid . '/edit', array('absolute' => TRUE)), t("Logged-in user redirected to user/UID/edit on accessing user/register"));
   }
 
   /**
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module
index 0b954ae1cbad..6e6bb4d8278b 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.module
+++ b/core/modules/system/tests/modules/menu_test/menu_test.module
@@ -537,7 +537,7 @@ function menu_test_static_variable($value = NULL) {
  * Implements hook_menu_site_status_alter().
  */
 function menu_test_menu_site_status_alter(&$menu_site_status, $path) {
-  // Allow access to ?q=menu_login_callback even if in maintenance mode.
+  // Allow access to menu_login_callback even if in maintenance mode.
   if ($menu_site_status == MENU_SITE_OFFLINE && $path == 'menu_login_callback') {
     $menu_site_status = MENU_SITE_ONLINE;
   }
diff --git a/core/modules/system/tests/modules/url_alter_test/url_alter_test.module b/core/modules/system/tests/modules/url_alter_test/url_alter_test.module
index 9287ff52307a..a5ca13deb4c6 100644
--- a/core/modules/system/tests/modules/url_alter_test/url_alter_test.module
+++ b/core/modules/system/tests/modules/url_alter_test/url_alter_test.module
@@ -30,8 +30,8 @@ function url_alter_test_foo() {
  * Implements hook_url_inbound_alter().
  */
 function url_alter_test_url_inbound_alter(&$path, $original_path, $path_language) {
-  if (!request_path() && !empty($_GET['q'])) {
-    drupal_set_message("\$_GET['q'] is non-empty with an empty request path.");
+  if (!request_path() && current_path()) {
+    drupal_set_message("current_path() is non-empty with an empty request path.");
   }
 
   // Rewrite user/username to user/uid.
diff --git a/core/modules/system/tests/path.test b/core/modules/system/tests/path.test
index fe6f73459fd0..f02a9b4b91a5 100644
--- a/core/modules/system/tests/path.test
+++ b/core/modules/system/tests/path.test
@@ -201,11 +201,11 @@ class UrlAlterFunctionalTest extends DrupalWebTestCase {
   }
 
   /**
-   * Tests that $_GET['q'] is initialized when the request path is empty.
+   * Tests that current_path() is initialized when the request path is empty.
    */
   function testGetQInitialized() {
     $this->drupalGet('');
-    $this->assertText("\$_GET['q'] is non-empty with an empty request path.", "\$_GET['q'] is initialized with an empty request path.");
+    $this->assertText("current_path() is non-empty with an empty request path.", "current_path() is initialized with an empty request path.");
   }
 
   /**
@@ -221,7 +221,7 @@ class UrlAlterFunctionalTest extends DrupalWebTestCase {
   protected function assertUrlOutboundAlter($original, $final) {
     // Test outbound altering.
     $result = url($original);
-    $base_path = base_path() . (variable_get('clean_url', '0') ? '' : '?q=');
+    $base_path = base_path() . $GLOBALS['script_path'];
     $result = substr($result, strlen($base_path));
     $this->assertIdentical($result, $final, t('Altered outbound URL %original, expected %final, and got %result.', array('%original' => $original, '%final' => $final, '%result' => $result)));
   }
diff --git a/core/modules/system/tests/session.test b/core/modules/system/tests/session.test
index 5cc8fe9476ea..ff0afb85fdaf 100644
--- a/core/modules/system/tests/session.test
+++ b/core/modules/system/tests/session.test
@@ -513,7 +513,7 @@ class SessionHttpsTestCase extends DrupalWebTestCase {
    */
   protected function httpsUrl($url) {
     global $base_url;
-    return $base_url . '/core/modules/system/tests/https.php?q=' . $url;
+    return $base_url . '/core/modules/system/tests/https.php/' . $url;
   }
 
   /**
@@ -527,7 +527,7 @@ class SessionHttpsTestCase extends DrupalWebTestCase {
    */
   protected function httpUrl($url) {
     global $base_url;
-    return $base_url . '/core/modules/system/tests/http.php?q=' . $url;
+    return $base_url . '/core/modules/system/tests/http.php/' . $url;
   }
 }
 
diff --git a/core/modules/system/tests/tablesort.test b/core/modules/system/tests/tablesort.test
index 9c068f861459..aaea5058b7af 100644
--- a/core/modules/system/tests/tablesort.test
+++ b/core/modules/system/tests/tablesort.test
@@ -49,7 +49,7 @@ class TableSortTest extends DrupalUnitTestCase {
     $headers = array('foo', 'bar', 'baz');
     // Reset $_GET to prevent parameters from Simpletest and Batch API ending
     // up in $ts['query'].
-    $_GET = array('q' => 'jahwohl');
+    $_GET = array();
     $expected_ts = array(
       'name' => 'foo',
       'sql' => '',
@@ -64,7 +64,6 @@ class TableSortTest extends DrupalUnitTestCase {
     // override the default.
 
     $_GET = array(
-      'q' => 'jahwohl',
       // This should not override the table order because only complex
       // headers are overridable.
       'order' => 'bar',
@@ -77,7 +76,6 @@ class TableSortTest extends DrupalUnitTestCase {
     // override the default.
 
     $_GET = array(
-      'q' => 'jahwohl',
       'sort' => 'DESC',
       // Add an unrelated parameter to ensure that tablesort will include
       // it in the links that it creates.
@@ -107,7 +105,6 @@ class TableSortTest extends DrupalUnitTestCase {
     );
     // Reset $_GET from previous assertion.
     $_GET = array(
-      'q' => 'jahwohl',
       'order' => '2',
     );
     $ts = tablesort_init($headers);
@@ -124,7 +121,6 @@ class TableSortTest extends DrupalUnitTestCase {
     // override the default.
 
     $_GET = array(
-      'q' => 'jahwohl',
       // This should not override the table order because this header does not
       // exist.
       'order' => 'bar',
@@ -144,7 +140,6 @@ class TableSortTest extends DrupalUnitTestCase {
     // override the default.
 
     $_GET = array(
-      'q' => 'jahwohl',
       'order' => '1',
       'sort' => 'ASC',
       // Add an unrelated parameter to ensure that tablesort will include
diff --git a/core/modules/system/tests/theme.test b/core/modules/system/tests/theme.test
index c1ba08f809fb..2a1959c77890 100644
--- a/core/modules/system/tests/theme.test
+++ b/core/modules/system/tests/theme.test
@@ -69,14 +69,14 @@ class ThemeUnitTest extends DrupalWebTestCase {
    * Ensure page-front template suggestion is added when on front page.
    */
   function testFrontPageThemeSuggestion() {
-    $q = $_GET['q'];
-    // Set $_GET['q'] to node because theme_get_suggestions() will query it to
-    // see if we are on the front page.
+    $original_path = _current_path();
+    // Set the current path to node because theme_get_suggestions() will query
+    // it to see if we are on the front page.
     variable_set('site_frontpage', 'node');
-    $_GET['q'] = 'node';
-    $suggestions = theme_get_suggestions(explode('/', $_GET['q']), 'page');
+    _current_path('node');
+    $suggestions = theme_get_suggestions(array('node'), 'page');
     // Set it back to not annoy the batch runner.
-    $_GET['q'] = $q;
+    _current_path($original_path);
     $this->assertTrue(in_array('page__front', $suggestions), t('Front page template was suggested.'));
   }
 
@@ -317,7 +317,7 @@ class ThemeFunctionsTestCase extends DrupalWebTestCase {
     // Required to verify the "active" class in expected links below, and
     // because the current path is different when running tests manually via
     // simpletest.module ('batch') and via the testing framework ('').
-    $_GET['q'] = variable_get('site_frontpage', 'user');
+    _current_path(variable_get('site_frontpage', 'user'));
 
     // Verify that a list of links is properly rendered.
     $variables = array();
diff --git a/core/modules/system/tests/upgrade/upgrade.test b/core/modules/system/tests/upgrade/upgrade.test
index e4045beb789b..52d78006bee8 100644
--- a/core/modules/system/tests/upgrade/upgrade.test
+++ b/core/modules/system/tests/upgrade/upgrade.test
@@ -78,7 +78,6 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
     $this->originalLanguageDefault = variable_get('language_default');
     $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
     $this->originalProfile = drupal_get_profile();
-    $clean_url_original = variable_get('clean_url', 0);
 
     // Unregister the registry.
     // This is required to make sure that the database layer works properly.
@@ -138,7 +137,6 @@ abstract class UpgradePathTestCase extends DrupalWebTestCase {
     drupal_save_session(FALSE);
 
     // Restore necessary variables.
-    $this->variable_set('clean_url', $clean_url_original);
     $this->variable_set('site_mail', 'simpletest@example.com');
 
     drupal_set_time_limit($this->timeLimit);
diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc
index a95b856416d2..9b230055526a 100644
--- a/core/modules/taxonomy/taxonomy.admin.inc
+++ b/core/modules/taxonomy/taxonomy.admin.inc
@@ -422,7 +422,7 @@ function taxonomy_overview_terms($form, &$form_state, TaxonomyVocabulary $vocabu
       '#type' => 'submit',
       '#value' => t('Reset to alphabetical')
     );
-    $form_state['redirect'] = array($_GET['q'], (isset($_GET['page']) ? array('query' => array('page' => $_GET['page'])) : array()));
+    $form_state['redirect'] = array(current_path(), (isset($_GET['page']) ? array('query' => array('page' => $_GET['page'])) : array()));
   }
 
   return $form;
@@ -784,7 +784,7 @@ function taxonomy_form_term($form, &$form_state, TaxonomyTerm $term = NULL, Taxo
     );
   }
   else {
-    $form_state['redirect'] = $_GET['q'];
+    $form_state['redirect'] = current_path();
   }
 
   return $form;
diff --git a/core/modules/update/update.compare.inc b/core/modules/update/update.compare.inc
index 3b1de4852822..46e98d422651 100644
--- a/core/modules/update/update.compare.inc
+++ b/core/modules/update/update.compare.inc
@@ -741,7 +741,6 @@ function update_project_cache($cid) {
 
   // On certain paths, we should clear the cache and recompute the projects for
   // update status of the site to avoid presenting stale information.
-  $q = $_GET['q'];
   $paths = array(
     'admin/modules',
     'admin/modules/update',
@@ -753,7 +752,7 @@ function update_project_cache($cid) {
     'admin/reports/status',
     'admin/reports/updates/check',
   );
-  if (in_array($q, $paths)) {
+  if (in_array(current_path(), $paths)) {
     _update_cache_clear($cid);
   }
   else {
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index 6142a209a2c0..44d86d6af619 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -107,7 +107,7 @@ function update_help($path, $arg) {
  */
 function update_init() {
   if (arg(0) == 'admin' && user_access('administer site configuration')) {
-    switch ($_GET['q']) {
+    switch (current_path()) {
       // These pages don't need additional nagging.
       case 'admin/appearance/update':
       case 'admin/appearance/install':
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index e6fa2dd1325a..4219728ab8b7 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -1025,7 +1025,7 @@ function user_account_form_validate($form, &$form_state) {
 }
 
 function user_login_block($form) {
-  $form['#action'] = url($_GET['q'], array('query' => drupal_get_destination()));
+  $form['#action'] = url(current_path(), array('query' => drupal_get_destination()));
   $form['#id'] = 'user-login-form';
   $form['#validate'] = user_login_default_validators();
   $form['#submit'][] = 'user_login_submit';
@@ -3557,7 +3557,7 @@ function user_register_form($form, &$form_state) {
   if ($admin) {
     // Redirect back to page which initiated the create request;
     // usually admin/people/create.
-    $form_state['redirect'] = $_GET['q'];
+    $form_state['redirect'] = current_path();
   }
 
   $form['actions'] = array('#type' => 'actions');
diff --git a/core/scripts/drupal.sh b/core/scripts/drupal.sh
index cf17e68bf03a..fb5e82c1061e 100755
--- a/core/scripts/drupal.sh
+++ b/core/scripts/drupal.sh
@@ -119,16 +119,15 @@
           $cmd = substr($path['path'], 1);
         }
         elseif (isset($path['path'])) {
-          if (!isset($_GET['q'])) {
-            $_REQUEST['q'] = $_GET['q'] = $path['path'];
-          }
+          $_SERVER['SCRIPT_NAME'] = '/' . $cmd;
+          $_SERVER['REQUEST_URI'] = $path['path'];
         }
 
         // display setup in verbose mode
         if ($_verbose_mode) {
           echo "Hostname set to: {$_SERVER['HTTP_HOST']}\n";
           echo "Script name set to: {$cmd}\n";
-          echo "Path set to: {$_GET['q']}\n";
+          echo "Path set to: {$path['path']}\n";
         }
       }
       break;
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 47bfd4893f87..644ff47fcf2e 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -155,7 +155,7 @@ function simpletest_script_help() {
 
               One or more tests to be run. By default, these are interpreted
               as the names of test groups as shown at
-              ?q=admin/config/development/testing.
+              admin/config/development/testing.
               These group names typically correspond to module names like "User"
               or "Profile" or "System", but there is also a group "XML-RPC".
               If --class is specified then these are interpreted as the names of
diff --git a/robots.txt b/robots.txt
index 90bec1e52b51..d590fbfa7c55 100644
--- a/robots.txt
+++ b/robots.txt
@@ -35,12 +35,12 @@ Disallow: /user/password/
 Disallow: /user/login/
 Disallow: /user/logout/
 # Paths (no clean URLs)
-Disallow: /?q=admin/
-Disallow: /?q=comment/reply/
-Disallow: /?q=filter/tips/
-Disallow: /?q=node/add/
-Disallow: /?q=search/
-Disallow: /?q=user/password/
-Disallow: /?q=user/register/
-Disallow: /?q=user/login/
-Disallow: /?q=user/logout/
+Disallow: /index.php/admin/
+Disallow: /index.php/comment/reply/
+Disallow: /index.php/filter/tips/
+Disallow: /index.php/node/add/
+Disallow: /index.php/search/
+Disallow: /index.php/user/password/
+Disallow: /index.php/user/register/
+Disallow: /index.php/user/login/
+Disallow: /index.php/user/logout/
diff --git a/web.config b/web.config
index e990e035f5f1..06e4cbe6eb87 100644
--- a/web.config
+++ b/web.config
@@ -57,7 +57,9 @@
         </rule>
     -->
 
-        <!-- Rewrite URLs of the form 'x' to the form 'index.php?q=x'. -->
+        <!-- Pass all requests not referring directly to files in the filesystem
+         to index.php. Clean URLs are handled in
+         drupal_environment_initialize(). -->
         <rule name="Short URLS" stopProcessing="true">
           <match url="^(.*)$" ignoreCase="false" />
           <conditions>
@@ -65,9 +67,6 @@
             <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
             <add input="{URL}" pattern="^/favicon.ico$" ignoreCase="false" negate="true" />
           </conditions>
-          <!-- Pass all requests not referring directly to files in the
-            filesystem to index.php. Clean URLs are handled in
-            drupal_environment_initialize(). -->
           <action type="Rewrite" url="index.php" />
         </rule>
       </rules>
-- 
GitLab