diff --git a/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php
index f99f8c5..1472f8f 100644
--- a/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php
+++ b/core/lib/Drupal/Core/Template/TwigPhpStorageCache.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Template;
 
+use Drupal\Component\Utility\Crypt;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\PhpStorage\PhpStorageFactory;
 
@@ -18,6 +19,11 @@
 class TwigPhpStorageCache implements \Twig_CacheInterface {
 
   /**
+   * The maximum length for each part of the cache key suffix.
+   */
+  const SUFFIX_SUBSTRING_LENGTH = 25;
+
+  /**
    * The cache object used for auto-refresh via mtime.
    *
    * @var \Drupal\Core\Cache\CacheBackendInterface
@@ -67,20 +73,30 @@ protected function storage() {
    * {@inheritdoc}
    */
   public function generateKey($name, $className) {
-    $hash = hash('sha256', $className);
-
     if (strpos($name, '{# inline_template_start #}') === 0) {
       // $name is an inline template, and can have characters that are not valid
-      // for a filename. $hash is unique for each inline template so we just use
-      // the generic name 'inline-template' here.
+      // for a filename. $suffix is unique for each inline template so we just
+      // use the generic name 'inline-template' here.
       $name = 'inline-template';
     }
     else {
       $name = basename($name);
     }
 
-    // The first part is what is invalidated.
-    return $this->templateCacheFilenamePrefix . '_' . $name . '_' . $hash;
+    // If the suffix is longer that 51 characters then we truncate it so the
+    // length of the filename can't grow beyond that. Windows only supports 255
+    // characters in a path. If the files directory is in the usual place of
+    // 'sites/default/files' then the maximum relative path of a twig file is
+    // 153 characters. We use 138 of that as follows:
+    // - 30 (/sites/default/files/php/twig/)
+    // - 9 (prefix + _)
+    // - 51 (suffix)
+    // - 48 (/ + hmac_hash + .php)
+    $suffix = substr($name, 0, self::SUFFIX_SUBSTRING_LENGTH) . '_';
+    $suffix .= substr(Crypt::hashBase64($className), 0, self::SUFFIX_SUBSTRING_LENGTH);
+
+    // The cache prefix is what gets invalidated.
+    return $this->templateCacheFilenamePrefix . '_' . $suffix;
   }
 
   /**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 6dc2663..8bae2d2 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -904,6 +904,23 @@ function system_requirements($phase) {
     }
   }
 
+  // Installations on Windows can run into limitations with MAX_PATH if the
+  // Drupal root directory is too deep in the filesystem. Generally this shows
+  // up in cached Twig templates and other public files with long directory or
+  // file names. There is no definite root directory depth below which Drupal is
+  // guaranteed to function correctly on Windows. Since problems are likely
+  // with more than 100 characters in the Drupal root path, show an error.
+  if (substr(PHP_OS, 0, 3) == 'WIN') {
+    $depth = strlen(realpath(DRUPAL_ROOT));
+    if ($depth > 100) {
+      $requirements['max_path_on_windows'] = [
+        'title' => t('Windows installation depth'),
+        'description' => t('The path length to the Drupal root directory (@depth characters) is greater than 100 characters. This may cause problems when running on a Windows server. You should install Drupal in a shorter path.', ['@depth' => $depth]),
+        'severity' => REQUIREMENT_ERROR,
+      ];
+    }
+  }
+
   return $requirements;
 }
 
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php
index c3e7af6..d698c4e 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/TwigEnvironmentTest.php
@@ -2,8 +2,10 @@
 
 namespace Drupal\KernelTests\Core\Theme;
 
+use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Site\Settings;
+use Drupal\Core\Template\TwigPhpStorageCache;
 use Drupal\KernelTests\KernelTestBase;
 
 /**
@@ -87,7 +89,7 @@ public function testInlineTemplate() {
 
     $cache = $environment->getCache();
     $class = $environment->getTemplateClass($name);
-    $expected = $prefix . '_inline-template' . '_' . hash('sha256', $class);
+    $expected = $prefix . '_inline-template_' . substr(Crypt::hashBase64($class), 0, TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH);
     $this->assertEqual($expected, $cache->generateKey($name, $class));
   }
 
@@ -116,6 +118,19 @@ public function testCacheFilename() {
     // static cache.
     $environment = \Drupal::service('twig');
 
+    // A template basename greater than the constant
+    // TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH should get truncated.
+    $cache = $environment->getCache();
+    $long_name = 'core/modules/system/templates/block--system-messages-block.html.twig';
+    $this->assertGreaterThan(TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH, strlen(basename($long_name)));
+    $class = $environment->getTemplateClass($long_name);
+    $key = $cache->generateKey($long_name, $class);
+    $hash = $this->container->getParameter('twig_extension_hash');
+    // The key should consist of the hash, two underscores, and two strings
+    // truncated to length TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH.
+    $expected = strlen($hash) + 2 + 2 * TwigPhpStorageCache::SUFFIX_SUBSTRING_LENGTH;
+    $this->assertEquals($expected, strlen($key));
+
     $original_filename = $environment->getCacheFilename('core/modules/system/templates/container.html.twig');
     \Drupal::getContainer()->set('twig', NULL);
 
