diff --git a/core/core.services.yml b/core/core.services.yml
index 5080841..7aab9a2 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -115,6 +115,13 @@ services:
     factory_method: get
     factory_service: cache_factory
     arguments: [discovery]
+  cache.url_generator:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin }
+    factory_method: get
+    factory_service: cache_factory
+    arguments: [url_generator]
   page_cache_request_policy:
     class: Drupal\Core\PageCache\DefaultRequestPolicy
     arguments: ['@session_configuration']
@@ -565,11 +572,17 @@ services:
     arguments: ['@route_filter.lazy_collector']
     tags:
       - { name: event_subscriber }
-  url_generator:
+  url_generator.uncached:
     class: Drupal\Core\Routing\UrlGenerator
+    public: false
     arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@config.factory', '@logger.channel.default', '@request_stack']
     calls:
       - [setContext, ['@?router.request_context']]
+  url_generator:
+    class: Drupal\Core\Routing\CachedUrlGenerator
+    arguments: ['@url_generator.uncached', '@cache.url_generator', '@router.route_provider', '@request_stack']
+    tags:
+      - { name: needs_destruction }
   unrouted_url_assembler:
     class: Drupal\Core\Utility\UnroutedUrlAssembler
     arguments: ['@request_stack', '@config.factory', '@path_processor_manager']
@@ -941,6 +954,7 @@ services:
     class: Drupal\Core\Access\RouteProcessorCsrf
     tags:
       - { name: route_processor_outbound }
+      - { name: event_subscriber }
     arguments: ['@csrf_token']
   transliteration:
     class: Drupal\Core\Transliteration\PhpTransliteration
diff --git a/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php b/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php
index 49873fa..7121f57 100644
--- a/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php
+++ b/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php
@@ -9,13 +9,16 @@
 
 use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
 use Drupal\Core\Access\CsrfTokenGenerator;
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RoutingEvents;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Route;
 
 /**
  * Processes the outbound route to handle the CSRF token.
  */
-class RouteProcessorCsrf implements OutboundRouteProcessorInterface {
+class RouteProcessorCsrf implements OutboundRouteProcessorInterface, EventSubscriberInterface {
 
   /**
    * The CSRF token generator.
@@ -50,4 +53,25 @@ public function processOutbound($route_name, Route $route, array &$parameters) {
     }
   }
 
+  /**
+   * Marks routes with _csrf_token as non-cacheable.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The route build event.
+   */
+  public function onRouteAlter(RouteBuildEvent $event) {
+    foreach ($event->getRouteCollection()->all() as $route) {
+      if ($route->hasRequirement('_csrf_token')) {
+        $route->setOption('_cacheable', FALSE);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[RoutingEvents::ALTER][] = ['onRouteAlter', -1000];
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php b/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php
new file mode 100644
index 0000000..805c32b
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/CachedUrlGenerator.php
@@ -0,0 +1,246 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\CachedUrlGenerator.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\DestructableInterface;
+use Symfony\Cmf\Component\Routing\RouteProviderInterface as SymfonyRouteProviderInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\RequestContext as SymfonyRequestContext;
+use Symfony\Component\Routing\Route as SymfonyRoute;
+
+/**
+ * Class used to wrap a UrlGenerator to provide caching of the generated values.
+ *
+ * Caching occurs per URI, so that each cache entry comprises the generated URLs
+ * from that URI.
+ */
+class CachedUrlGenerator implements DestructableInterface, CachedUrlGeneratorInterface {
+
+  /**
+   * The wrapped URL generator
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface
+   */
+  protected $urlGenerator;
+
+  /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cache;
+
+  /**
+   * A nested array of cached URLs keyed by request URI.
+   *
+   * Each nested array contains URLs cached by route name or path.
+   *
+   * @var array
+   */
+  protected $cachedUrls = array();
+
+  /**
+   * The route provider.
+   *
+   * @var \Symfony\Cmf\Component\Routing\RouteProviderInterface
+   */
+  protected $routeProvider;
+
+  /**
+   * Whether the cache needs to be written.
+   *
+   * @var boolean
+   */
+  protected $cacheNeedsWriting = FALSE;
+
+  /**
+   * The request stack service.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * Cache prefix for route names.
+   */
+  const ROUTE_CACHE_PREFIX = 'route::';
+
+  /**
+   * Cache prefix for paths.
+   */
+  const PATH_CACHE_PREFIX = 'path::';
+
+  /**
+   * Constructs a \Drupal\Core\Routing\CachedUrlGenerator.
+   *
+   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
+   *   The wrapped URL generator
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
+   *   The cache backend.
+   * @param \Symfony\Cmf\Component\Routing\RouteProviderInterface $route_provider
+   *   The route provider.
+   * @param Symfony\Component\HttpFoundation\RequestStack
+   *   The request stack.
+   */
+  public function __construct(UrlGeneratorInterface $url_generator, CacheBackendInterface $cache, SymfonyRouteProviderInterface $route_provider, RequestStack $request_stack) {
+    $this->urlGenerator = $url_generator;
+    $this->cache = $cache;
+    $this->routeProvider = $route_provider;
+    $this->requestStack = $request_stack;
+
+    // Select cache based on the current request URI. We store one entry for
+    // each URI.
+    $cache_key = $request_stack->getCurrentRequest()->getUri();
+    $this->cachedUrls[$cache_key] = [];
+
+    // Retrieve stored URLs.
+    $cached = $this->cache->get($cache_key);
+    if ($cached) {
+      $this->cachedUrls[$cache_key] = $cached->data;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clearCache() {
+    foreach (array_keys($this->cachedUrls) as $cache_key) {
+      $this->cache->delete($cache_key);
+    }
+    $this->cachedUrls = array();
+  }
+
+  /**
+   * Writes the cache of generated URLs.
+   */
+  protected function writeCache() {
+    if ($this->cacheNeedsWriting && !empty($this->cachedUrls)) {
+      foreach (array_keys($this->cachedUrls) as $cache_key) {
+        // Set the URL cache to expire in 24 hours.
+        $expire = REQUEST_TIME + (60 * 60 * 24);
+        $this->cache->set($cache_key, $this->cachedUrls[$cache_key], $expire);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function generate($name, $parameters = array(), $absolute = FALSE) {
+    $options = array();
+    // We essentially inline the implementation from the Drupal UrlGenerator
+    // and avoid setting $options so that we increase the likelihood of caching.
+    if ($absolute) {
+      $options['absolute'] = $absolute;
+    }
+    return $this->generateFromRoute($name, $parameters, $options);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function generateFromPath($path = NULL, $options = array()) {
+    $key = static::PATH_CACHE_PREFIX . hash('sha256', $path . serialize($options));
+    $cache_key = $this->requestStack->getCurrentRequest()->getUri();
+    if (!isset($this->cachedUrls[$cache_key][$key])) {
+      $this->cachedUrls[$cache_key][$key] = $this->urlGenerator->generateFromPath($path, $options);
+      $this->cacheNeedsWriting = TRUE;
+    }
+    return $this->cachedUrls[$cache_key][$key];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function generateFromRoute($name, $parameters = array(), $options = array()) {
+    // In some cases $name may be a Route object, rather than a string.
+    $key = static::ROUTE_CACHE_PREFIX . hash('sha256', serialize($name) . serialize($options) . serialize($parameters));
+    $cache_key = $this->requestStack->getCurrentRequest()->getUri();
+    if (!isset($this->cachedUrls[$cache_key][$key])) {
+      $generated_url = $this->urlGenerator->generateFromRoute($name, $parameters, $options);
+      $route = $this->getRoute($name);
+      if ($route->hasOption('_cacheable') && !$route->getOption('_cacheable')) {
+        return $generated_url;
+      }
+      else {
+        $this->cachedUrls[$cache_key][$key] = $generated_url;
+        $this->cacheNeedsWriting = TRUE;
+      }
+    }
+    return $this->cachedUrls[$cache_key][$key];
+  }
+
+  /**
+   * Find the route using the provided route name.
+   *
+   * @param string $name
+   *   The route name to fetch
+   *
+   * @return \Symfony\Component\Routing\Route
+   *   The found route.
+   *
+   * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException
+   *   Thrown if there is no route with that name in this repository.
+   *
+   * @see \Drupal\Core\Routing\RouteProviderInterface
+   */
+  protected function getRoute($name) {
+    if ($name instanceof SymfonyRoute) {
+      $route = $name;
+    }
+    elseif (NULL === $route = clone $this->routeProvider->getRouteByName($name)) {
+      throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name));
+    }
+    return $route;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function supports($name) {
+    return $this->urlGenerator->supports($name);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteDebugMessage($name, array $parameters = array()) {
+    return $this->urlGenerator->getRouteDebugMessage($name, $parameters);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function destruct() {
+    $this->writeCache();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setContext(SymfonyRequestContext $context) {
+    $this->urlGenerator->setContext($context);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getContext() {
+    return $this->urlGenerator->getContext();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPathFromRoute($name, $parameters = array()) {
+    return $this->urlGenerator->getPathFromRoute($name, $parameters);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/CachedUrlGeneratorInterface.php b/core/lib/Drupal/Core/Routing/CachedUrlGeneratorInterface.php
new file mode 100644
index 0000000..d9bbbf3
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/CachedUrlGeneratorInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\CachedUrlGeneratorInterface.
+ */
+
+namespace Drupal\Core\Routing;
+
+/**
+ * Defines an interface for generating a URL from a route or system path.
+ *
+ * Provides additional methods for generators that cache the URLs.
+ */
+interface CachedUrlGeneratorInterface extends UrlGeneratorInterface {
+
+  /**
+   * Clears the caches of the URL generator.
+   */
+  public function clearCache();
+
+}
diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index 1926d44..fcf311a 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -138,7 +138,7 @@ protected function getInternalPathFromRoute(SymfonyRoute $route, $parameters = a
   /**
    * {@inheritdoc}
    */
-  public function generate($name, $parameters = array(), $absolute = FALSE) {
+  public function generate($name, $parameters = array(), $absolute = self::ABSOLUTE_PATH) {
     $options['absolute'] = $absolute;
     return $this->generateFromRoute($name, $parameters, $options);
   }
diff --git a/core/modules/file/src/Tests/DownloadTest.php b/core/modules/file/src/Tests/DownloadTest.php
index 9de1b3a..32f607f 100644
--- a/core/modules/file/src/Tests/DownloadTest.php
+++ b/core/modules/file/src/Tests/DownloadTest.php
@@ -101,6 +101,8 @@ protected function doPrivateFileTransferTest() {
    */
   function testFileCreateUrl() {
 
+    $urlGenerator = \Drupal::service('url_generator');
+
     // 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.
     // @see http://www.php.net/manual/function.rawurlencode.php#86506
@@ -125,6 +127,7 @@ function testFileCreateUrl() {
       $base_path = $request->getSchemeAndHttpHost() . $request->getBasePath();
       $this->checkUrl('public', '', $basename, $base_path . '/' . file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath() . '/' . $basename_encoded);
       $this->checkUrl('private', '', $basename, $base_path . '/' . $script_path . 'system/files/' . $basename_encoded);
+      $urlGenerator->clearCache();
     }
     $this->assertEqual(file_create_url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='), 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==', t('Generated URL matches expected URL.'));
   }
diff --git a/core/modules/language/src/Tests/LanguageConfigurationTest.php b/core/modules/language/src/Tests/LanguageConfigurationTest.php
index a5efb2f..11cc51e 100644
--- a/core/modules/language/src/Tests/LanguageConfigurationTest.php
+++ b/core/modules/language/src/Tests/LanguageConfigurationTest.php
@@ -65,6 +65,8 @@ function testLanguageConfiguration() {
     );
     $this->drupalPostForm(NULL, $edit, t('Save configuration'));
     $this->rebuildContainer();
+    // Clear cached urls in the local process too.
+    $this->container->get('url_generator')->clearCache();
     $this->assertFieldChecked('edit-site-default-language-fr', 'Default language updated.');
     $this->assertUrl(\Drupal::url('entity.configurable_language.collection', [], ['absolute' => TRUE, 'langcode' => 'fr']), [], 'Correct page redirection.');
 
diff --git a/core/modules/language/src/Tests/LanguageListTest.php b/core/modules/language/src/Tests/LanguageListTest.php
index 8ec846c..3c002fe 100644
--- a/core/modules/language/src/Tests/LanguageListTest.php
+++ b/core/modules/language/src/Tests/LanguageListTest.php
@@ -53,6 +53,8 @@ function testLanguageList() {
       'direction' => Language::DIRECTION_LTR,
     );
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
+    // Clear cached urls in the local process too.
+    $this->container->get('url_generator')->clearCache();
     $this->assertUrl(\Drupal::url('entity.configurable_language.collection', [], ['absolute' => TRUE]));
     $this->assertRaw('"edit-languages-' . $langcode .'-weight"', 'Language code found.');
     $this->assertText(t($name), 'Test language added.');
@@ -69,6 +71,8 @@ function testLanguageList() {
       'site_default_language' => $langcode,
     );
     $this->drupalPostForm(NULL, $edit, t('Save configuration'));
+    // Clear cached urls in the local process too.
+    $this->container->get('url_generator')->clearCache();
     $this->rebuildContainer();
     $this->assertNoFieldChecked('edit-site-default-language-en', 'Default language updated.');
     $this->assertUrl(\Drupal::url('entity.configurable_language.collection', [], ['absolute' => TRUE, 'language' => $language]));
@@ -96,6 +100,8 @@ function testLanguageList() {
     );
     $this->drupalPostForm($path, $edit, t('Save configuration'));
     $this->rebuildContainer();
+    // Clear cached urls in the local process too.
+    $this->container->get('url_generator')->clearCache();
     // Ensure 'delete' link works.
     $this->drupalGet('admin/config/regional/language');
     $this->clickLink(t('Delete'));
@@ -117,6 +123,8 @@ function testLanguageList() {
     $this->drupalGet('admin/config/regional/language/delete/' . $langcode);
     $this->assertResponse(404, 'Language no longer found.');
 
+    // Clear cached urls in the local process too.
+    $this->container->get('url_generator')->clearCache();
     // Delete French.
     $this->drupalPostForm('admin/config/regional/language/delete/fr', array(), t('Delete'));
     // Make sure the "language_count" state has been updated correctly.
diff --git a/core/modules/language/src/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/src/Tests/LanguageUILanguageNegotiationTest.php
index e584fb7..3c6d193 100644
--- a/core/modules/language/src/Tests/LanguageUILanguageNegotiationTest.php
+++ b/core/modules/language/src/Tests/LanguageUILanguageNegotiationTest.php
@@ -474,9 +474,10 @@ function testLanguageDomain() {
     $correct_link = 'https://' . $link;
     $this->assertTrue($italian_url == $correct_link, format_string('The right HTTPS URL (via options) (@url) in accordance with the chosen language', array('@url' => $italian_url)));
 
-    // Test HTTPS via current URL scheme.
+    // Switch from HTTP to HTTPS support.
     $request = Request::create('', 'GET', array(), array(), array(), array('HTTPS' => 'on'));
     $this->container->get('request_stack')->push($request);
+
     $italian_url = Url::fromRoute('system.admin', [], ['language' => $languages['it']])->toString();
     $correct_link = 'https://' . $link;
     $this->assertTrue($italian_url == $correct_link, format_string('The right URL (via current URL scheme) (@url) in accordance with the chosen language', array('@url' => $italian_url)));
diff --git a/core/modules/rest/src/Routing/ResourceRoutes.php b/core/modules/rest/src/Routing/ResourceRoutes.php
index db439dc..c711bbe 100644
--- a/core/modules/rest/src/Routing/ResourceRoutes.php
+++ b/core/modules/rest/src/Routing/ResourceRoutes.php
@@ -71,6 +71,10 @@ protected function alterRoutes(RouteCollection $collection) {
 
     // Iterate over all enabled resource plugins.
     foreach ($enabled_resources as $id => $enabled_methods) {
+      // rest.settings.yml includes non-existing plugins at the moment.
+      if (!$this->manager->hasDefinition($id)) {
+        continue;
+      }
       $plugin = $this->manager->getInstance(array('id' => $id));
 
       foreach ($plugin->routes() as $name => $route) {
diff --git a/core/modules/system/src/Form/RegionalForm.php b/core/modules/system/src/Form/RegionalForm.php
index 827f74f..1472fa8 100644
--- a/core/modules/system/src/Form/RegionalForm.php
+++ b/core/modules/system/src/Form/RegionalForm.php
@@ -8,9 +8,11 @@
 namespace Drupal\system\Form;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Locale\CountryManagerInterface;
-use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Routing\CachedUrlGeneratorInterface;
+use Drupal\Core\Routing\UrlGeneratorInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -26,16 +28,26 @@ class RegionalForm extends ConfigFormBase {
   protected $countryManager;
 
   /**
+   * The URL generator.
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface
+   */
+  protected $urlGenerator;
+
+  /**
    * Constructs a RegionalForm object.
    *
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The factory for configuration objects.
    * @param \Drupal\Core\Locale\CountryManagerInterface $country_manager
    *   The country manager.
+   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
+   *   The url generator.
    */
-  public function __construct(ConfigFactoryInterface $config_factory, CountryManagerInterface $country_manager) {
+  public function __construct(ConfigFactoryInterface $config_factory, CountryManagerInterface $country_manager, UrlGeneratorInterface $url_generator) {
     parent::__construct($config_factory);
     $this->countryManager = $country_manager;
+    $this->urlGenerator = $url_generator;
   }
 
   /**
@@ -44,7 +56,8 @@ public function __construct(ConfigFactoryInterface $config_factory, CountryManag
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('config.factory'),
-      $container->get('country_manager')
+      $container->get('country_manager'),
+      $container->get('url_generator')
     );
   }
 
@@ -159,6 +172,10 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       ->set('timezone.user.default', $form_state->getValue('user_default_timezone'))
       ->save();
 
+    if ($this->urlGenerator instanceof CachedUrlGeneratorInterface) {
+      $this->urlGenerator->clearCache();
+    }
+
     parent::submitForm($form, $form_state);
   }
 
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index fecfd249..8ec0fd0 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -387,6 +387,8 @@ system.theme_settings_theme:
 
 '<current>':
   path: '<current>'
+  options:
+    _cacheable: false
 
 system.modules_uninstall:
   path: '/admin/modules/uninstall'
diff --git a/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php b/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php
index 48f77b9..8d908b2 100644
--- a/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php
+++ b/core/tests/Drupal/Tests/Core/Access/RouteProcessorCsrfTest.php
@@ -7,9 +7,11 @@
 
 namespace Drupal\Tests\Core\Access;
 
+use Drupal\Core\Routing\RouteBuildEvent;
 use Drupal\Tests\UnitTestCase;
 use Drupal\Core\Access\RouteProcessorCsrf;
 use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
 
 /**
  * @coversDefaultClass \Drupal\Core\Access\RouteProcessorCsrf
@@ -103,4 +105,22 @@ public function testProcessOutboundDynamicTwo() {
     $this->assertNull($this->processor->processOutbound('test', $route, $parameters));
   }
 
+  /**
+   * @covers ::onRouteAlter
+   */
+  public function testOnRouteAlter() {
+    $route_cacheable = new Route('/example-1');
+    $route_not_cacheable = new Route('/example-2', [], ['_csrf_token' => 'TRUE']);
+
+    $collection = new RouteCollection();
+    $collection->add('example_1', $route_cacheable);
+    $collection->add('example_2', $route_not_cacheable);
+
+    $event = new RouteBuildEvent($collection);
+
+    $this->processor->onRouteAlter($event);
+    $this->assertFalse($collection->get('example_1')->hasOption('_cacheable'));
+    $this->assertFalse($collection->get('example_2')->getOption('_cacheable'));
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorIntegrationTest.php b/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorIntegrationTest.php
new file mode 100644
index 0000000..0965091
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorIntegrationTest.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Routing\CachehdUrlGeneratorIntegrationTest.
+ */
+
+namespace Drupal\Tests\Core\Routing;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Confirms Urls are stored in cache.
+ *
+ * @group routing
+ */
+class CachedUrlGeneratorIntegrationTest extends WebTestBase {
+
+  /**
+   * Confirms initial population of cache.
+   */
+  public function testCacheIsPopulated() {
+
+    // Cache key and url.
+    $name = $this->getAbsoluteUrl('/');
+
+    // Populate the cache.
+    $this->drupalGet($name);
+
+    $cache = $this->container->get('cache.url_generator');
+    // BAD - Use knowledge of an internal implementation detail to extract the
+    // cache data!
+    $data = $cache->get($name)->data;
+    $this->assertFalse(empty($data), 'The cache has been primed.');
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php b/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php
new file mode 100644
index 0000000..14fafce
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Routing/CachedUrlGeneratorTest.php
@@ -0,0 +1,245 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Routing\CachedUrlGeneratorTest.
+ */
+
+namespace Drupal\Tests\Core\Routing;
+
+use Drupal\Core\Routing\CachedUrlGenerator;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Tests the cache url generator.
+ *
+ * @coversDefaultClass \Drupal\Core\Routing\CachedUrlGenerator
+ * @group Routing
+ */
+class CachedUrlGeneratorTest extends UnitTestCase {
+
+  /**
+   * The wrapped URL generator.
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $urlGenerator;
+
+  /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cache;
+
+  /**
+   * Language manager for retrieving the URL language type.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $languageManager;
+
+  /**
+   * Route Provider front-end for all Drupal-stored routes.
+   *
+   * @var \Symfony\Cmf\Component\Routing\RouteProviderInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $routeProvider;
+
+  /**
+   * The actual tested cached url generator.
+   *
+   * @var \Drupal\Core\Routing\CachedUrlGenerator
+   */
+  protected $cachedUrlGenerator;
+
+  /**
+   * The route object.
+   *
+   * @var \Symfony\Component\Routing\Route|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $route;
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
+    $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->routeProvider = $this->getMock('Symfony\Cmf\Component\Routing\RouteProviderInterface');
+    $this->route = $this->getMockBuilder('Symfony\Component\Routing\Route')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->routeProvider->expects($this->any())
+      ->method('getRouteByName')
+      ->willReturn($this->route);
+    $this->requestStack = new RequestStack();
+    $this->requestStack->push(new Request());
+    $this->cachedUrlGenerator = new CachedUrlGenerator($this->urlGenerator, $this->cache, $this->routeProvider, $this->requestStack);
+  }
+
+  /**
+   * Tests the generate method.
+   *
+   * @see \Drupal\Core\Routing\CachedUrlGenerator::generate()
+   */
+  public function testGenerate() {
+    $this->urlGenerator->expects($this->once())
+      ->method('generateFromRoute')
+      ->with('test_route')
+      ->will($this->returnValue('test-route-1'));
+    // First call will prime the cache.
+    $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromRoute('test_route'));
+    // Second call will fetch from the cache.
+    $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromRoute('test_route'));
+  }
+
+  /**
+   * Tests the generate method with the same route name but different parameters.
+   *
+   * @see \Drupal\Core\Routing\CachedUrlGenerator::generate()
+   */
+  public function testGenerateWithDifferentParameters() {
+    // We are generating URL's four times but since two of them are cached,
+    // we expect that generateFromRoute will only be called twice.
+    $this->urlGenerator->expects($this->exactly(2))
+      ->method('generateFromRoute')
+      ->will($this->returnValueMap(array(
+        array('test_route', array('key' => 'value1'), array(), 'test-route-1/value1'),
+        array('test_route', array('key' => 'value2'), array(), 'test-route-1/value2'),
+      )));
+
+    // First call will prime the cache for key=value1.
+    $this->assertEquals('test-route-1/value1', $this->cachedUrlGenerator->generate('test_route', array('key' => 'value1')));
+    // Second call will fetch from the cache.
+    $this->assertEquals('test-route-1/value1', $this->cachedUrlGenerator->generate('test_route', array('key' => 'value1')));
+    // Third call uses the same route but since the parameters are different,
+    // the cache is not used and the generateFromRoute method will be called
+    // for the second time.
+    $this->assertEquals('test-route-1/value2', $this->cachedUrlGenerator->generate('test_route', array('key' => 'value2')));
+    // Fourth call will fetch from the cache as it is now primed.
+    $this->assertEquals('test-route-1/value2', $this->cachedUrlGenerator->generate('test_route', array('key' => 'value2')));
+  }
+
+  /**
+   * Tests the generateFromPath method.
+   *
+   * @see \Drupal\Core\Routing\CachedUrlGenerator::generateFromPath()
+   */
+  public function testGenerateFromPath() {
+    $this->urlGenerator->expects($this->once())
+      ->method('generateFromPath')
+      ->with('test-route-1')
+      ->will($this->returnValue('test-route-1'));
+
+    // First call will prime the cache.
+    $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1'));
+    // Second call will fetch from the cache.
+    $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1'));
+  }
+
+  /**
+   * Tests the generate method with the same path but different options
+   *
+   * @see \Drupal\Core\Routing\CachedUrlGenerator::generateFromPath()
+   */
+  public function testGenerateFromPathWithDifferentParameters() {
+    // We are generating URL's six times but since three of them are cached,
+    // we expect that generateFromPath will only be called three times.
+    $this->urlGenerator->expects($this->exactly(3))
+      ->method('generateFromPath')
+      ->will($this->returnValueMap(array(
+        array('test-route-1', array('absolute' => TRUE), 'http://localhost/test-route-1'),
+        array('test-route-1', array('absolute' => FALSE), 'test-route-1'),
+        array('test-route-1', array('absolute' => TRUE), 'http://localhost/test-route-1'),
+      )));
+
+    // First call will prime the cache.
+    $this->assertEquals('http://localhost/test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => TRUE)));
+    // Second call will fetch from cache.
+    $this->assertEquals('http://localhost/test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => TRUE)));
+    // Third call uses the same path but since the options are different, the
+    // cache is not used and the generateFromPath method will be called for
+    // the second time.
+    $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => FALSE)));
+    // Fourth call will fetch from cache.
+    $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => FALSE)));
+    $new_request = Request::create('/foo');
+    $this->requestStack->push($new_request);
+    // Fifth call uses the first path again but a new request.
+    $this->assertEquals('http://localhost/test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => TRUE)));
+    // Sixth call will fetch from cache.
+    $this->assertEquals('http://localhost/test-route-1', $this->cachedUrlGenerator->generateFromPath('test-route-1', array('absolute' => TRUE)));
+  }
+
+
+  /**
+   * Tests the generateFromRoute method.
+   *
+   * @see \Drupal\Core\Routing\CachedUrlGenerator::generateFromRoute()
+   */
+  public function testGenerateFromRoute() {
+    $this->urlGenerator->expects($this->once())
+      ->method('generateFromRoute')
+      ->with('test_route')
+      ->will($this->returnValue('test-route-1'));
+
+    // First call will prime the cache.
+    $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromRoute('test_route'));
+    // Second call will fetch from the cache.
+    $this->assertEquals('test-route-1', $this->cachedUrlGenerator->generateFromRoute('test_route'));
+  }
+
+  /**
+   * Tests the generateFromRoute method with the same path, different options.
+   *
+   * @see \Drupal\Core\Routing\CachedUrlGenerator::generateFromRoute()
+   */
+  public function testGenerateFromRouteWithDifferentParameters() {
+    // We are generating URL's eight times but since two of them are cached,
+    // we expect that generateFromRoute will only be called four times.
+    $this->urlGenerator->expects($this->exactly(4))
+      ->method('generateFromRoute')
+      ->will($this->returnValueMap(array(
+        array('test_route', array('key' => 'value1'), array(), 'test-route-1/value1'),
+        array('test_route', array('key' => 'value1'), array('absolute' => TRUE), 'http://localhost/test-route-1/value1'),
+        array('test_route', array('key' => 'value2'), array(), 'test-route-1/value2'),
+        array('test_route', array('key' => 'value2'), array('absolute' => TRUE), 'http://localhost/test-route-1/value2'),
+      )));
+
+    // First call will prime the cache.
+    $this->assertEquals('test-route-1/value1', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value1')));
+    // Second call will fetch from the cache.
+    $this->assertEquals('test-route-1/value1', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value1')));
+    // Third call uses the same route but since the options are different, the
+    // cache is not used and the generateFromRoute method will be called for
+    // the second time.
+    $this->assertEquals('http://localhost/test-route-1/value1', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value1'), array('absolute' => TRUE)));
+    // Fourth call will fetch from the cache.
+    $this->assertEquals('http://localhost/test-route-1/value1', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value1'), array('absolute' => TRUE)));
+    // Fifth call uses the same route but since the parameters are different,
+    // the cache is not used and the generateFromRoute method will be called for
+    // the third time.
+    $this->assertEquals('test-route-1/value2', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value2')));
+    // Sixth call will fetch from the cache.
+    $this->assertEquals('test-route-1/value2', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value2')));
+    // Seventh call uses the same route but since the options are different, the
+    // cache is not used and the generateFromRoute method will be called for
+    // the fourth time.
+    $this->assertEquals('http://localhost/test-route-1/value2', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value2'), array('absolute' => TRUE)));
+    // Eighth call will fetch from the cache.
+    $this->assertEquals('http://localhost/test-route-1/value2', $this->cachedUrlGenerator->generateFromRoute('test_route', array('key' => 'value2'), array('absolute' => TRUE)));
+  }
+
+}
