diff --git a/core/lib/Drupal/Core/Update/UpdateKernel.php b/core/lib/Drupal/Core/Update/UpdateKernel.php
new file mode 100644
index 0000000..5ae068c
--- /dev/null
+++ b/core/lib/Drupal/Core/Update/UpdateKernel.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Update\UpdateKernel.
+ */
+
+namespace Drupal\Core\Update;
+
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Session\AnonymousUserSession;
+use Drupal\Core\Site\Settings;
+use Drupal\Core\StackMiddleware\Session;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\ParameterBag;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
+use Symfony\Component\Routing\Route;
+
+class UpdateKernel extends DrupalKernel {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
+    try {
+      static::bootEnvironment();
+
+      $this->initializeSettings($request);
+      $this->boot();
+
+      $container = $this->getContainer();
+      /** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */
+      $request_stack = $container->get('request_stack');
+      $request_stack->push($request);
+
+      $this->preHandle($request);
+      $this->bootSession($request, $type);
+      $result = $this->handleRaw($request);
+      $this->shutdownSession($request);
+
+      return $result;
+    }
+    catch (\Exception $e) {
+      return $this->handleException($e, $request, $type);
+    }
+  }
+
+  protected function handleRaw(Request $request) {
+
+    $container = $this->getContainer();
+    /** @var \Drupal\Core\Authentication\AuthenticationManager $authentication_manager */
+    $authentication_manager = $container->get('authentication');
+    $account = $authentication_manager->authenticate($request) ?: new AnonymousUserSession();
+
+    /** @var \Drupal\system\Access\DbUpdateAccessCheck $db_update_access */
+    $db_update_access = $container->get('access_check.db_update');
+
+    if (!$db_update_access->access($account) && !Settings::get('update_free_access', FALSE)) {
+      throw new AccessDeniedHttpException('In order to run update.php you need to either be logged in as admin or have set $update_free_access in your settings.php.');
+    }
+
+    /** @var \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver */
+    $controller_resolver = $container->get('controller_resolver');
+
+    /** @var callable $db_update_controller */
+    $db_update_controller = $controller_resolver->getControllerFromDefinition('\Drupal\system\Controller\DbUpdateController::handle');
+
+    $this->setupRequestMatch($request);
+
+    $arguments = $controller_resolver->getArguments($request, $db_update_controller);
+    return call_user_func_array($db_update_controller, $arguments);
+  }
+
+  /**
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   * @param $type
+   */
+  protected function bootSession(Request $request, $type) {
+    if ($type === self::MASTER_REQUEST && PHP_SAPI !== 'cli') {
+      $container = $this->getContainer();
+      /** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
+      $session = $container->get('session');
+      $session->start();
+      $request->setSession($session);
+    }
+  }
+
+  /**
+   */
+  protected function setupRequestMatch(Request $request) {
+    $path = $request->getPathInfo();
+    $args = explode('/', ltrim($path, '/'));
+
+    $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'system.db_update');
+    $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/update.php/{op}', ['op' => 'info'], ['_access' => 'TRUE']));
+    $op = $args[0] ?: 'info';
+    $request->attributes->set('op', $op);
+    $request->attributes->set('_raw_variables', new ParameterBag(['op' => $op]));
+  }
+
+  protected function shutdownSession(Request $request) {
+    if ($request->hasSession()) {
+      $request->getSession()->save();
+    }
+  }
+
+}
diff --git a/core/modules/system/src/Controller/DbUpdateController.php b/core/modules/system/src/Controller/DbUpdateController.php
index c2e195e..5fdbfee 100644
--- a/core/modules/system/src/Controller/DbUpdateController.php
+++ b/core/modules/system/src/Controller/DbUpdateController.php
@@ -166,7 +166,7 @@ public function handle($op, Request $request) {
       switch ($op) {
         case 'selection':
           $regions['sidebar_first'] = $this->updateTasksList('selection');
-          $output = $this->selection();
+          $output = $this->selection($request);
           break;
 
         case 'run':
@@ -176,12 +176,12 @@ public function handle($op, Request $request) {
 
         case 'info':
           $regions['sidebar_first'] = $this->updateTasksList('info');
-          $output = $this->info();
+          $output = $this->info($request);
           break;
 
         case 'results':
           $regions['sidebar_first'] = $this->updateTasksList('results');
-          $output = $this->results();
+          $output = $this->results($request);
           break;
 
         // Regular batch ops : defer to batch processing API.
@@ -207,7 +207,7 @@ public function handle($op, Request $request) {
    * @return array
    *   A render array.
    */
-  protected function info() {
+  protected function info(Request $request) {
     // Change query-strings on css/js files to enforce reload for all users.
     _drupal_flush_css_js();
     // Flush the cache of all data for the update status module.
@@ -233,12 +233,11 @@ protected function info() {
       '#markup' => '<p>' . $this->t('When you have performed the steps above, you may proceed.') . '</p>',
     );
 
-    $url = new Url('system.db_update', array('op' => 'selection'));
     $build['link'] = array(
       '#type' => 'link',
       '#title' => $this->t('Continue'),
       '#attributes' => array('class' => array('button', 'button--primary')),
-      '#url' => $url,
+      '#url' => Url::fromUri($request->getUriForPath('/selection')),
     );
     return $build;
   }
@@ -246,10 +245,13 @@ protected function info() {
   /**
    * Renders a list of available database updates.
    *
-   * @return array
-   *   A render array.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return array A render array.
+   * A render array.
    */
-  protected function selection() {
+  protected function selection(Request $request) {
     // Make sure there is no stale theme registry.
     $this->cache->deleteAll();
 
@@ -342,7 +344,7 @@ protected function selection() {
       unset($build);
       $build['links'] = array(
         '#theme' => 'links',
-        '#links' => $this->helpfulLinks(),
+        '#links' => $this->helpfulLinks($request),
       );
 
       // No updates to run, so caches won't get flushed later.  Clear them now.
@@ -364,7 +366,8 @@ protected function selection() {
       else {
         $build['start']['#title'] = $this->formatPlural($count, '1 pending update', '@count pending updates');
       }
-      $url = new Url('system.db_update', array('op' => 'run'));
+      $base_url = str_replace('/update.php', '', $request->getBaseUrl());
+      $url = (new Url('system.db_update', array('op' => 'run')))->setOption('base_url', $base_url);
       $build['link'] = array(
         '#type' => 'link',
         '#title' => $this->t('Apply pending updates'),
@@ -380,10 +383,13 @@ protected function selection() {
   /**
    * Displays results of the update script with any accompanying errors.
    *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
    * @return array
    *   A render array.
    */
-  protected function results() {
+  protected function results(Request $request) {
     // Report end result.
     $dblog_exists = $this->moduleHandler->moduleExists('dblog');
     if ($dblog_exists && $this->account->hasPermission('access site reports')) {
@@ -420,7 +426,7 @@ protected function results() {
     );
     $build['links'] = array(
       '#theme' => 'links',
-      '#links' => $this->helpfulLinks(),
+      '#links' => $this->helpfulLinks($request),
     );
 
     // Output a list of info messages.
@@ -603,7 +609,7 @@ protected function triggerBatch(Request $request) {
     );
     batch_set($batch);
 
-    return batch_process('update.php/results', Url::fromRoute('system.db_update', array('op' => 'start')));
+    return batch_process(Url::fromUri($request->getUriForPath('/results')), Url::fromUri($request->getUriForPath('/start')));
   }
 
   /**
@@ -640,18 +646,22 @@ public static function batchFinished($success, $results, $operations) {
   /**
    * Provides links to the homepage and administration pages.
    *
-   * @return array
-   *   An array of links.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return array An array of links.
+   * An array of links.
    */
-  protected function helpfulLinks() {
+  protected function helpfulLinks(Request $request) {
+    $base_url = str_replace('/update.php', '', $request->getBaseUrl());
     $links['front'] = array(
       'title' => $this->t('Front page'),
-      'url' => Url::fromRoute('<front>'),
+      'url' => Url::fromRoute('<front>')->setOption('base_url', $base_url),
     );
     if ($this->account->hasPermission('access administration pages')) {
       $links['admin-pages'] = array(
         'title' => $this->t('Administration pages'),
-        'url' => Url::fromRoute('system.admin'),
+        'url' => Url::fromRoute('system.admin')->setOption('base_url', $base_url),
       );
     }
     return $links;
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index a656ab3..9ef3720 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -451,13 +451,9 @@ system.batch_page.json:
 system.db_update:
   path: '/update.php/{op}'
   defaults:
-    _title: 'Drupal database update'
-    _controller: '\Drupal\system\Controller\DbUpdateController::handle'
     op: 'info'
-  options:
-    _maintenance_access: TRUE
   requirements:
-    _access_system_update: 'TRUE'
+    _access: 'TRUE'
 
 system.admin_content:
   path: '/admin/content'
diff --git a/update.php b/update.php
new file mode 100644
index 0000000..04125a3
--- /dev/null
+++ b/update.php
@@ -0,0 +1,14 @@
+<?php
+
+use Drupal\Core\Update\UpdateKernel;
+use Symfony\Component\HttpFoundation\Request;
+
+$autoloader = require_once 'autoload.php';
+
+$kernel = new UpdateKernel('prod', $autoloader);
+$request = Request::createFromGlobals();
+
+$response = $kernel->handle($request);
+$response->send();
+
+$kernel->terminate($request, $response);
