diff --git a/persistent_login.services.yml b/persistent_login.services.yml
index 89957e2..0101ef1 100644
--- a/persistent_login.services.yml
+++ b/persistent_login.services.yml
@@ -2,11 +2,19 @@ services:
   logger.channel.persistent_login:
     parent: logger.channel_base
     arguments: ['persistent_login']
+  persistent_login.cookie_helper:
+    class: Drupal\persistent_login\CookieHelper
+    arguments: ['@session_configuration']
   persistent_login.token_manager:
     class: Drupal\persistent_login\TokenManager
     arguments: ['@database', '@config.factory', '@csrf_token', '@logger.channel.persistent_login']
   persistent_login.token_handler:
     class: Drupal\persistent_login\EventSubscriber\TokenHandler
-    arguments: ['@persistent_login.token_manager', '@session_configuration', '@entity.manager']
+    arguments: ['@persistent_login.token_manager', '@persistent_login.cookie_helper', '@session_configuration', '@entity.manager']
     tags:
       - { name: event_subscriber }
+  persistent_login.page_cache_request_policy.pending_persistent_login:
+    class: Drupal\persistent_login\PageCache\RequestPolicy\PendingPersistentLogin
+    arguments: ['@persistent_login.cookie_helper', '@session_configuration']
+    tags:
+      - { name: page_cache_request_policy }
diff --git a/src/CookieHelper.php b/src/CookieHelper.php
new file mode 100644
index 0000000..1769f6c
--- /dev/null
+++ b/src/CookieHelper.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Drupal\persistent_login;
+
+use Drupal\Core\Session\SessionConfigurationInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Cookie helper service.
+ */
+class CookieHelper implements CookieHelperInterface {
+
+  /**
+   * The session configuration.
+   *
+   * @var \Drupal\Core\Session\SessionConfigurationInterface
+   */
+  protected $sessionConfiguration;
+
+  /**
+   * Instantiates a new CookieHelper instance.
+   *
+   * @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
+   *   The session configuration.
+   */
+  public function __construct(SessionConfigurationInterface $session_configuration) {
+    $this->sessionConfiguration = $session_configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCookieName(Request $request) {
+    $sessionConfigurationSettings = $this->sessionConfiguration->getOptions($request);
+    return 'PL' . substr($sessionConfigurationSettings['name'], 4);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasCookie(Request $request) {
+    return $request->cookies->has($this->getCookieName($request));
+  }
+
+}
diff --git a/src/CookieHelperInterface.php b/src/CookieHelperInterface.php
new file mode 100644
index 0000000..9f3fe69
--- /dev/null
+++ b/src/CookieHelperInterface.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\persistent_login;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Interface for Cookie helper services.
+ */
+interface CookieHelperInterface {
+
+  /**
+   * Returns the name of the persistent login cookie.
+   *
+   * Value is based on the session cookie name.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return string
+   *   The cookie name.
+   */
+  public function getCookieName(Request $request);
+
+  /**
+   * Checks if a request contains a persistent login cookie.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return bool
+   *   TRUE if the request provides a persistent login cookie.
+   */
+  public function hasCookie(Request $request);
+
+}
diff --git a/src/EventSubscriber/TokenHandler.php b/src/EventSubscriber/TokenHandler.php
index b66c066..fce41ce 100644
--- a/src/EventSubscriber/TokenHandler.php
+++ b/src/EventSubscriber/TokenHandler.php
@@ -4,6 +4,7 @@ namespace Drupal\persistent_login\EventSubscriber;
 
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Session\SessionConfigurationInterface;
+use Drupal\persistent_login\CookieHelperInterface;
 use Drupal\persistent_login\PersistentToken;
 use Drupal\persistent_login\TokenException;
 use Drupal\persistent_login\TokenManager;
@@ -29,6 +30,13 @@ class TokenHandler implements EventSubscriberInterface {
   protected $tokenManager;
 
   /**
+   * The cookie helper service.
+   *
+   * @var \Drupal\persistent_login\CookieHelper
+   */
+  protected $cookieHelper;
+
+  /**
    * The session configuration.
    *
    * @var \Drupal\Core\Session\SessionConfigurationInterface
@@ -54,6 +62,8 @@ class TokenHandler implements EventSubscriberInterface {
    *
    * @param \Drupal\persistent_login\TokenManager $token_manager
    *   The token manager service.
+   * @param \Drupal\persistent_login\CookieHelperInterface $cookie_helper
+   *   The cookie helper service.
    * @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
    *   The session configuration.
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_manager
@@ -61,10 +71,12 @@ class TokenHandler implements EventSubscriberInterface {
    */
   public function __construct(
     TokenManager $token_manager,
+    CookieHelperInterface $cookie_helper,
     SessionConfigurationInterface $session_configuration,
     EntityTypeManagerInterface $entity_manager
   ) {
     $this->tokenManager = $token_manager;
+    $this->cookieHelper = $cookie_helper;
     $this->sessionConfiguration = $session_configuration;
     $this->entityManager = $entity_manager;
   }
@@ -99,7 +111,7 @@ class TokenHandler implements EventSubscriberInterface {
 
     $request = $event->getRequest();
 
-    if ($this->hasCookie($request)) {
+    if ($this->cookieHelper->hasCookie($request)) {
       $this->token = $this->getTokenFromCookie($request);
 
       // Only validate the token if a user session has not been started.
@@ -139,7 +151,7 @@ class TokenHandler implements EventSubscriberInterface {
         $this->token = $this->tokenManager->updateToken($this->token);
         $response->headers->setCookie(
           new Cookie(
-            $this->getCookieName($request),
+            $this->cookieHelper->getCookieName($request),
             $this->token,
             $this->token->getExpiry(),
             '/',  // TODO Path should probably match the base path.
@@ -152,7 +164,7 @@ class TokenHandler implements EventSubscriberInterface {
         // Invalid token, or manually cleared token (e.g. user logged out).
         $this->tokenManager->deleteToken($this->token);
         $response->headers->clearCookie(
-          $this->getCookieName($request),
+          $this->cookieHelper->getCookieName($request),
           '/', // TODO Path should probably match the base path.
           $sessionOptions['cookie_domain'],
           $sessionOptions['cookie_secure']
@@ -165,35 +177,6 @@ class TokenHandler implements EventSubscriberInterface {
   }
 
   /**
-   * Get the name of the persistent login cookie.
-   *
-   * Value is based on the session cookie name.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request.
-   *
-   * @return string
-   *   The cookie name.
-   */
-  protected function getCookieName(Request $request) {
-    $sessionConfigurationSettings = $this->sessionConfiguration->getOptions($request);
-    return 'PL' . substr($sessionConfigurationSettings['name'], 4);
-  }
-
-  /**
-   * Check if a request contains a persistent login cookie.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request.
-   *
-   * @return bool
-   *   True if the request provides a persistent login cookie.
-   */
-  public function hasCookie(Request $request) {
-    return $request->cookies->has($this->getCookieName($request));
-  }
-
-  /**
    * Create a token object from the cookie provided in the request.
    *
    * @param \Symfony\Component\HttpFoundation\Request $request
@@ -203,7 +186,7 @@ class TokenHandler implements EventSubscriberInterface {
    *   A new PersistentToken object.
    */
   public function getTokenFromCookie(Request $request) {
-    return PersistentToken::createFromString($request->cookies->get($this->getCookieName($request)));
+    return PersistentToken::createFromString($request->cookies->get($this->cookieHelper->getCookieName($request)));
   }
 
   /**
diff --git a/src/PageCache/RequestPolicy/PendingPersistentLogin.php b/src/PageCache/RequestPolicy/PendingPersistentLogin.php
new file mode 100644
index 0000000..66979df
--- /dev/null
+++ b/src/PageCache/RequestPolicy/PendingPersistentLogin.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Drupal\persistent_login\PageCache\RequestPolicy;
+
+use Drupal\Core\PageCache\RequestPolicyInterface;
+use Drupal\Core\Session\SessionConfigurationInterface;
+use Drupal\persistent_login\CookieHelperInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * A policy preventing caching of pages with pending persistent login cookies.
+ *
+ * We have a "pending" situation when no session cookie is present but there is
+ * a persistent login one. This happens when a user is logged in with a
+ * persistent login and closes the browser window. At that moment the session
+ * cookies will be cleared, and only the persistent cookie will be present when
+ * the browser is reopened.
+ *
+ * In the first kernel request event, the cookie will be validated and the user
+ * will be logged back to the system. From that moment on, the session cookie
+ * will be present again and this policy won't apply anymore. If the cookie is
+ * invalid, it will be removed during the response event, again making this
+ * policy not applicable on subsequent page loads.
+ */
+class PendingPersistentLogin implements RequestPolicyInterface {
+
+  /**
+   * The session configuration.
+   *
+   * @var \Drupal\Core\Session\SessionConfigurationInterface
+   */
+  protected $sessionConfiguration;
+
+  /**
+   * The cookie helper service.
+   *
+   * @var \Drupal\persistent_login\CookieHelperInterface
+   */
+  protected $cookieHelper;
+
+  /**
+   * Instantiates a new PendingPersistentLogin object.
+   *
+   * @param \Drupal\persistent_login\CookieHelperInterface $cookie_helper
+   *   The cookie helper service.
+   * @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
+   *   The session configuration.
+   */
+  public function __construct(CookieHelperInterface $cookie_helper, SessionConfigurationInterface $session_configuration) {
+    $this->cookieHelper = $cookie_helper;
+    $this->sessionConfiguration = $session_configuration;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function check(Request $request) {
+    // Prevent the serving of cached pages if a persistent login cookie is
+    // present, but the session is missing. This means the user has closed and
+    // reopened the browser window. By serving a non-cached page we can ensure
+    // that the persistent login cookie will be validated and the session cookie
+    // will be regenerated.
+    if (!$this->sessionConfiguration->hasSession($request) && $this->cookieHelper->hasCookie($request)) {
+      return static::DENY;
+    }
+  }
+
+}
diff --git a/tests/src/Functional/PersistentLoginTest.php b/tests/src/Functional/PersistentLoginTest.php
new file mode 100644
index 0000000..f91ab0f
--- /dev/null
+++ b/tests/src/Functional/PersistentLoginTest.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Drupal\Tests\persistent_login\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests the persistent login functionality.
+ *
+ * @group persistent_login
+ */
+class PersistentLoginTest extends BrowserTestBase {
+
+  /**
+   * A test user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $user;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['persistent_login'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Create a test user.
+    $this->user = $this->createUser([], 'Garnett Tyrell');
+  }
+
+  /**
+   * Tests whether a user can be persistently logged in.
+   *
+   * @param bool $remember_me
+   *   Whether or not the "Remember me" option should be checked when logging
+   *   in.
+   *
+   * @dataProvider loginProvider
+   */
+  public function testPersistentLogin($remember_me) {
+    // Since we are not logged in yet, the homepage should show a "Log in" link.
+    // The reason we are testing the visibility of the "Log in" link rather than
+    // inspecting the session cookies, is because this way we can also validate
+    // that the page cache is correctly cleared in addition to checking if the
+    // user is logged in or not.
+    $this->assertTrue($this->homepageHasLoginForm());
+
+    // Log in through the UI.
+    $this->drupalGet('user');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->submitForm([
+      'name' => $this->user->getAccountName(),
+      'pass' => $this->user->passRaw,
+      'persistent_login' => $remember_me,
+    ], t('Log in'));
+
+    // Check that the homepage now doesn't show the "Log in" link any more.
+    $this->assertFalse($this->homepageHasLoginForm());
+
+    // Simulate the user closing the browser window and reopening it, by
+    // clearing the session cookies.
+    $this->restartSession();
+
+    // The "Log in" link should now only be shown on the homepage when the
+    // "Remember me" option was enabled.
+    $this->assertEquals($remember_me, !$this->homepageHasLoginForm());
+  }
+
+  /**
+   * Emulates closing and re-opening of the browser.
+   */
+  public function restartSession() {
+    $cookie_jar = $this->getCookieJar();
+
+    // We are going to keep all the cookies with a valid expire time. The
+    // \Symfony\Component\BrowserKit\CookieJar::all() method already removes
+    // expired cookies, so we need to strip the ones with a null expire time,
+    // which are the one that are going to be deleted when the browser is
+    // closed.
+    $persistent = [];
+    foreach ($cookie_jar->all() as $cookie) {
+      if (!is_null($cookie->getExpiresTime())) {
+        $persistent[] = $cookie;
+      }
+    }
+
+    $this->getSession()->restart();
+    $cookie_jar = $this->getCookieJar();
+
+    foreach ($persistent as $cookie) {
+      $cookie_jar->set($cookie);
+    }
+  }
+
+  /**
+   * Returns the jar which contains the cookies for the current session.
+   *
+   * @return \Symfony\Component\BrowserKit\CookieJar
+   *   The cookie jar.
+   */
+  protected function getCookieJar() {
+    return $this->getSession()->getDriver()->getClient()->getCookieJar();
+  }
+
+  /**
+   * Returns whether or not the login form is displayed on the homepage.
+   *
+   * @return bool
+   *   Whether or not the login form is displayed.
+   */
+  protected function homepageHasLoginForm() {
+    $this->drupalGet('<front>');
+    return (bool) $this->getSession()->getPage()->findButton('Log in');
+  }
+
+  /**
+   * Data provider for testPersistentLogin().
+   *
+   * @return array
+   *   An array of test cases. Each test case is an array containing a boolean
+   *   that indicates whether or not the "Remember me" option should be checked
+   *   when logging in.
+   */
+  public function loginProvider() {
+    return [
+      [
+        // When the "Remember me" functionality is not enabled, the user should
+        // not be logged in after starting a new session.
+        FALSE,
+      ],
+      [
+        // When the "Remember me" functionality is enabled, the user should be
+        // logged in after starting a new session.
+        TRUE,
+      ],
+    ];
+  }
+
+}
