 core/core.services.yml                             |  1 +
 core/modules/big_pipe/big_pipe.services.yml        |  2 +-
 .../src/Render/Placeholder/BigPipeStrategy.php     | 48 ++++++++++++++++------
 core/modules/big_pipe/src/Tests/BigPipeTest.php    | 18 ++++++++
 .../Render/Placeholder/BigPipeStrategyTest.php     |  6 ++-
 5 files changed, 61 insertions(+), 14 deletions(-)

diff --git a/core/core.services.yml b/core/core.services.yml
index 6049d3b..490c4a2 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -14,6 +14,7 @@ parameters:
       max-age: 0
       contexts: ['session', 'user']
       tags: []
+    debug: false
   factory.keyvalue:
     default: keyvalue.database
   http.response.debug_cacheability_headers: false
diff --git a/core/modules/big_pipe/big_pipe.services.yml b/core/modules/big_pipe/big_pipe.services.yml
index 5235037..1dd851e 100644
--- a/core/modules/big_pipe/big_pipe.services.yml
+++ b/core/modules/big_pipe/big_pipe.services.yml
@@ -6,7 +6,7 @@ services:
     arguments: ['@big_pipe']
   placeholder_strategy.big_pipe:
     class: Drupal\big_pipe\Render\Placeholder\BigPipeStrategy
-    arguments: ['@session_configuration', '@request_stack', '@current_route_match']
+    arguments: ['@session_configuration', '@request_stack', '@current_route_match', '%renderer.config%']
     tags:
       - { name: placeholder_strategy, priority: 0 }
   big_pipe:
diff --git a/core/modules/big_pipe/src/Render/Placeholder/BigPipeStrategy.php b/core/modules/big_pipe/src/Render/Placeholder/BigPipeStrategy.php
index 91ad604..cce11ce 100644
--- a/core/modules/big_pipe/src/Render/Placeholder/BigPipeStrategy.php
+++ b/core/modules/big_pipe/src/Render/Placeholder/BigPipeStrategy.php
@@ -86,6 +86,13 @@ class BigPipeStrategy implements PlaceholderStrategyInterface {
   protected $routeMatch;
 
   /**
+   * The renderer configuration array.
+   *
+   * @var array
+   */
+  protected $rendererConfig;
+
+  /**
    * Constructs a new BigPipeStrategy class.
    *
    * @param \Drupal\Core\Session\SessionConfigurationInterface $session_configuration
@@ -94,11 +101,14 @@ class BigPipeStrategy implements PlaceholderStrategyInterface {
    *   The request stack.
    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
    *   The current route match.
+   * @param array $renderer_config
+   *   The renderer configuration array.
    */
-  public function __construct(SessionConfigurationInterface $session_configuration, RequestStack $request_stack, RouteMatchInterface $route_match) {
+  public function __construct(SessionConfigurationInterface $session_configuration, RequestStack $request_stack, RouteMatchInterface $route_match, array $renderer_config) {
     $this->sessionConfiguration = $session_configuration;
     $this->requestStack = $request_stack;
     $this->routeMatch = $route_match;
+    $this->rendererConfig = $renderer_config;
   }
 
   /**
@@ -150,16 +160,16 @@ protected function doProcessPlaceholders(array $placeholders) {
       // @see \Drupal\Core\Form\FormBuilder::renderFormTokenPlaceholder()
       // @see \Drupal\Core\Form\FormBuilder::renderPlaceholderFormAction()
       if ($placeholder[0] !== '<' || $placeholder !== Html::normalize($placeholder)) {
-        $overridden_placeholders[$placeholder] = static::createBigPipeNoJsPlaceholder($placeholder, $placeholder_elements, TRUE);
+        $overridden_placeholders[$placeholder] = $this->createBigPipeNoJsPlaceholder($placeholder, $placeholder_elements, TRUE);
       }
       else {
         // If the current request/session doesn't have JavaScript, fall back to
         // no-JS BigPipe.
         if ($this->requestStack->getCurrentRequest()->cookies->has(static::NOJS_COOKIE)) {
-          $overridden_placeholders[$placeholder] = static::createBigPipeNoJsPlaceholder($placeholder, $placeholder_elements, FALSE);
+          $overridden_placeholders[$placeholder] = $this->createBigPipeNoJsPlaceholder($placeholder, $placeholder_elements, FALSE);
         }
         else {
-          $overridden_placeholders[$placeholder] = static::createBigPipeJsPlaceholder($placeholder, $placeholder_elements);
+          $overridden_placeholders[$placeholder] = $this->createBigPipeJsPlaceholder($placeholder, $placeholder_elements);
         }
         $overridden_placeholders[$placeholder]['#cache']['contexts'][] = 'cookies:' . static::NOJS_COOKIE;
       }
@@ -179,8 +189,8 @@ protected function doProcessPlaceholders(array $placeholders) {
    * @return array
    *   The resulting BigPipe JS placeholder render array.
    */
-  protected static function createBigPipeJsPlaceholder($original_placeholder, array $placeholder_render_array) {
-    $big_pipe_placeholder_id = static::generateBigPipePlaceholderId($original_placeholder, $placeholder_render_array);
+  protected function createBigPipeJsPlaceholder($original_placeholder, array $placeholder_render_array) {
+    $big_pipe_placeholder_id = $this->generateBigPipePlaceholderId($original_placeholder, $placeholder_render_array);
 
     return [
       '#markup' => '<div data-big-pipe-placeholder-id="' . Html::escape($big_pipe_placeholder_id) . '"></div>',
@@ -219,9 +229,9 @@ protected static function createBigPipeJsPlaceholder($original_placeholder, arra
    * @return array
    *   The resulting BigPipe no-JS placeholder render array.
    */
-  protected static function createBigPipeNoJsPlaceholder($original_placeholder, array $placeholder_render_array, $placeholder_must_be_attribute_safe = FALSE) {
+  protected function createBigPipeNoJsPlaceholder($original_placeholder, array $placeholder_render_array, $placeholder_must_be_attribute_safe = FALSE) {
     if (!$placeholder_must_be_attribute_safe) {
-      $big_pipe_placeholder = '<div data-big-pipe-nojs-placeholder-id="' . Html::escape(static::generateBigPipePlaceholderId($original_placeholder, $placeholder_render_array)) . '"></div>';
+      $big_pipe_placeholder = '<div data-big-pipe-nojs-placeholder-id="' . Html::escape($this->generateBigPipePlaceholderId($original_placeholder, $placeholder_render_array)) . '"></div>';
     }
     else {
       $big_pipe_placeholder = 'big_pipe_nojs_placeholder_attribute_safe:' . Html::escape($original_placeholder);
@@ -254,14 +264,28 @@ protected static function createBigPipeNoJsPlaceholder($original_placeholder, ar
    * @return string
    *   The generated BigPipe placeholder ID.
    */
-  protected static function generateBigPipePlaceholderId($original_placeholder, array $placeholder_render_array) {
+  protected function generateBigPipePlaceholderId($original_placeholder, array $placeholder_render_array) {
     // Generate a BigPipe placeholder ID (to be used by BigPipe's JavaScript).
     // @see \Drupal\Core\Render\PlaceholderGenerator::createPlaceholder()
     if (isset($placeholder_render_array['#lazy_builder'])) {
-      $callback = $placeholder_render_array['#lazy_builder'][0];
-      $arguments = $placeholder_render_array['#lazy_builder'][1];
+      $debug = !empty($this->rendererConfig['debug']);
+
       $token = hash('crc32b', serialize($placeholder_render_array));
-      return UrlHelper::buildQuery(['callback' => $callback, 'args' => $arguments, 'token' => $token]);
+      // When debug mode is on, generate a placeholder ID that lists the lazy
+      // builder for easier debugging. Otherwise, just use a consistently hashed
+      // token as the placeholder ID.
+      if ($debug) {
+        $callback = $placeholder_render_array['#lazy_builder'][0];
+        $arguments = $placeholder_render_array['#lazy_builder'][1];
+        return UrlHelper::buildQuery([
+          'callback' => $callback,
+          'args' => $arguments,
+          'token' => $token
+        ]);
+      }
+      else {
+        return $token;
+      }
     }
     // When the placeholder's render array is not using a #lazy_builder,
     // anything could be in there: only #lazy_builder has a strict contract that
diff --git a/core/modules/big_pipe/src/Tests/BigPipeTest.php b/core/modules/big_pipe/src/Tests/BigPipeTest.php
index f947c46..cccb25e 100644
--- a/core/modules/big_pipe/src/Tests/BigPipeTest.php
+++ b/core/modules/big_pipe/src/Tests/BigPipeTest.php
@@ -48,6 +48,24 @@ protected function setUp() {
     // $this->maximumMetaRefreshCount and $this->metaRefreshCount.
     // @see doMetaRefresh()
     $this->maximumMetaRefreshCount = 0;
+
+    // This does not actually work.
+    /*
+    if (!$this->isInChildSite()) {
+      file_put_contents($this->siteDirectory . '/settings.php', "\n\$settings['container_yamls'][] = " . $this->siteDirectory . "'/testing.services.yml';\n", FILE_APPEND);
+      $yaml = <<<EOD
+  parameters:
+    renderer.config:
+      required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions']
+      auto_placeholder_conditions:
+        max-age: 0
+        contexts: ['session', 'user']
+        tags: []
+      debug: true
+EOD;
+      file_put_contents($this->siteDirectory . '/testing.services.yml', $yaml);
+    }
+    */
   }
 
   /**
diff --git a/core/modules/big_pipe/tests/src/Unit/Render/Placeholder/BigPipeStrategyTest.php b/core/modules/big_pipe/tests/src/Unit/Render/Placeholder/BigPipeStrategyTest.php
index f0f40ba..ebf2c57 100644
--- a/core/modules/big_pipe/tests/src/Unit/Render/Placeholder/BigPipeStrategyTest.php
+++ b/core/modules/big_pipe/tests/src/Unit/Render/Placeholder/BigPipeStrategyTest.php
@@ -44,7 +44,11 @@ public function testProcessPlaceholders(array $placeholders, $method, $route_mat
     $route_match->getRouteObject()
       ->willReturn($route);
 
-    $big_pipe_strategy = new BigPipeStrategy($session_configuration->reveal(), $request_stack->reveal(), $route_match->reveal());
+    // @todo don't hardcode debug=true, instead test:
+    // 1. 'debug'=true
+    // 2. 'debug'=false
+    // 3. 'debug' is NOT SET (BC)
+    $big_pipe_strategy = new BigPipeStrategy($session_configuration->reveal(), $request_stack->reveal(), $route_match->reveal(), ['debug' => TRUE]);
     $processed_placeholders = $big_pipe_strategy->processPlaceholders($placeholders);
 
     if ($request->isMethodSafe() && !$route_match_has_no_big_pipe_option && $request_has_session) {
