 core/core.services.yml                             |   6 +-
 .../Session/CacheabilityBubblingAccountProxy.php   | 171 +++++++++++++++++++++
 core/modules/user/user.services.yml                |   2 +-
 3 files changed, 177 insertions(+), 2 deletions(-)

diff --git a/core/core.services.yml b/core/core.services.yml
index 1aadb75..48522ea 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1362,8 +1362,12 @@ services:
   user_permissions_hash_generator:
     class: Drupal\Core\Session\PermissionsHashGenerator
     arguments: ['@private_key', '@cache.default', '@cache.static']
-  current_user:
+  current_user.non_bubbling:
     class: Drupal\Core\Session\AccountProxy
+    public: false
+  current_user:
+    class: Drupal\Core\Session\CacheabilityBubblingAccountProxy
+    arguments: ['@current_user.non_bubbling', '@renderer']
   session_configuration:
     class: Drupal\Core\Session\SessionConfiguration
     arguments: ['%session.storage.options%']
diff --git a/core/lib/Drupal/Core/Session/CacheabilityBubblingAccountProxy.php b/core/lib/Drupal/Core/Session/CacheabilityBubblingAccountProxy.php
new file mode 100644
index 0000000..0812e98
--- /dev/null
+++ b/core/lib/Drupal/Core/Session/CacheabilityBubblingAccountProxy.php
@@ -0,0 +1,171 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Session\CacheabilityBubblingAccountProxy.
+ */
+
+namespace Drupal\Core\Session;
+
+use Drupal\Core\Render\RendererInterface;
+
+/**
+ * Decorator for the account proxy, which bubbles user-specific cacheability.
+ *
+ * Implements a decorator for the account proxy that allows to automatically
+ * bubble the current user's cache tag and the 'user' cache context. This allows
+ * Drupal 8 to cache aggressively yet still
+ *
+ * @see \Drupal\Core\Render\MetadataBubblingUrlGenerator
+ *
+ * @todo Remove before Drupal 9.0.0.
+ */
+class CacheabilityBubblingAccountProxy implements AccountProxyInterface {
+
+  /**
+   * The non-bubbling account proxy.
+   *
+   * @var \Drupal\Core\Session\AccountProxyInterface
+   */
+  protected $accountProxy;
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * Constructs a new bubbling account proxy.
+   *
+   * @param \Drupal\Core\Session\AccountProxyInterface $account_proxy
+   *   The non-bubbling account proxy.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   */
+  public function __construct(AccountProxyInterface $account_proxy, RendererInterface $renderer) {
+    $this->accountProxy = $account_proxy;
+    $this->renderer = $renderer;
+  }
+
+  /**
+   * Bubbles the current user's cache tag and the 'user' cache context.
+   */
+  protected function bubble() {
+    if ($this->renderer->hasRenderContext()) {
+      $build = [
+        '#cache' => [
+          'tags' => ['user:' . $this->id()],
+          'contexts' => ['user']
+        ],
+      ];
+      $this->renderer->render($build);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setAccount(AccountInterface $account) {
+    return $this->accountProxy->setAccount($account);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAccount() {
+    return $this->accountProxy->getAccount();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function id() {
+    return $this->accountProxy->id();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoles($exclude_locked_roles = FALSE) {
+    return $this->accountProxy->getRoles($exclude_locked_roles);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasPermission($permission) {
+    return $this->accountProxy->hasPermission($permission);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isAuthenticated() {
+    return $this->accountProxy->isAuthenticated();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isAnonymous() {
+    return $this->accountProxy->isAnonymous();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPreferredLangcode($fallback_to_default = TRUE) {
+    $this->bubble();
+    return $this->accountProxy->getPreferredLangcode($fallback_to_default);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPreferredAdminLangcode($fallback_to_default = TRUE) {
+    $this->bubble();
+    return $this->accountProxy->getPreferredAdminLangcode($fallback_to_default);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getUserName() {
+    $this->bubble();
+    return $this->accountProxy->getUsername();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getEmail() {
+    $this->bubble();
+    return $this->accountProxy->getEmail();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTimeZone() {
+    $this->bubble();
+    return $this->accountProxy->getTimeZone();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLastAccessedTime() {
+    $this->bubble();
+    return $this->accountProxy->getLastAccessedTime();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setInitialAccountId($account_id) {
+    return $this->accountProxy->setInitialAccountId($account_id);
+  }
+
+}
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index 8fc9bf5..3ece5f4 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -42,7 +42,7 @@ services:
       - { name: event_subscriber }
   theme.negotiator.admin_theme:
     class: Drupal\user\Theme\AdminNegotiator
-    arguments: ['@current_user', '@config.factory', '@entity.manager', '@router.admin_context']
+    arguments: ['@current_user.non_bubbling', '@config.factory', '@entity.manager', '@router.admin_context']
     tags:
       - { name: theme_negotiator, priority: -40 }
   user.auth:
