diff --git a/includes/common.inc b/includes/common.inc
index 27fa190..c85b5c3 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -2683,24 +2683,59 @@ function drupal_deliver_html_page($page_callback_result) {
  * react to the closing of the page by calling hook_exit().
  */
 function drupal_page_footer() {
-  global $user;
+  drupal_close_request();
+}
 
-  module_invoke_all('exit');
+/**
+ * Close the current request gracefully and attempt to run shutdown handlers
+ * after freeing the connection if possible.
+ */
+function drupal_close_request($destination = NULL) {
+  // Exit hooks must run before session commit, and session commit must happen
+  // before the flush. Once page is flushed, headers will be send and session
+  // cookies cannot be sent anymore.
+  if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
+    module_invoke_all('exit', $destination);
+  }
 
-  // Commit the user session, if needed.
+  // Session commit must happen before the page cache. When a cached page is
+  // sent headers are sent along with it. For the very same reason as upper,
+  // session cookies cannot be sent after page cache happens.
   drupal_session_commit();
 
-  if (variable_get('cache', 0) && ($cache = drupal_page_set_cache())) {
+  if (!$destination && variable_get('cache', 0) && ($cache = drupal_page_set_cache())) {
     drupal_serve_page_from_cache($cache);
   }
-  else {
-    ob_flush();
+
+  if (function_exists('fastcgi_finish_request')) {
+    fastcgi_finish_request();
+  }
+  // This implementations comes from the Symfony HttpFoundation component
+  // and credit goes to them. For more information refer to:
+  // https://github.com/symfony/HttpFoundation/blob/master/Response.php#L318
+  else if ('cli' !== PHP_SAPI) {
+    // ob_get_level() never returns 0 on some Windows configurations, so if
+    // the level is the same two times in a row, the loop should be stopped.
+    $previous = null;
+    $obStatus = ob_get_status(1);
+    while (($level = ob_get_level()) > 0 && $level !== $previous) {
+      $previous = $level;
+      if ($obStatus[$level - 1] && isset($obStatus[$level - 1]['del']) && $obStatus[$level - 1]['del']) {
+        ob_end_flush();
+      }
+    }
+    flush();
   }
 
+  // Write all remaining caches after the process has been detached or most
+  // of the page has already been sent to the HTTPd output buffer for sending
+  // to the client browser.
   _registry_check_code(REGISTRY_WRITE_LOOKUP_CACHE);
   drupal_cache_system_paths();
   module_implements_write_cache();
   system_run_automated_cron();
+
+  exit;
 }
 
 /**
@@ -2716,13 +2751,7 @@ function drupal_page_footer() {
  *   This should be passed along to hook_exit() implementations.
  */
 function drupal_exit($destination = NULL) {
-  if (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) {
-    if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
-      module_invoke_all('exit', $destination);
-    }
-    drupal_session_commit();
-  }
-  exit;
+  drupal_close_request($destination);
 }
 
 /**
