smithery.ai

drupal-dynamic-cache

Dynamic Page Cache and BigPipe module behavior in Drupal. Use when asked about authenticated user caching, auto-placeholdering, lazy builders, BigPipe streaming, X-Drupal-Dynamic-Cache header, or why content shows UNCACHEABLE status. Covers interaction between caching layers.

First seen May 1, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,211 B
  • docs SUMMARY.md 304 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Dynamic Page Cache & BigPipe

These modules cache pages for authenticated users while handling personalized content via placeholders.

When to Use

  • Improving performance for logged-in users
  • Debugging UNCACHEABLE responses
  • Implementing personalized content that doesn't break caching
  • Understanding BigPipe streaming behavior

How Dynamic Page Cache Works

Request → Check cache
├── HIT: Serve cached structure → Replace placeholders → Response
└── MISS: Render page → Auto-placeholder dynamic parts → Cache → Response

Response Headers

Header Values Meaning
X-Drupal-Dynamic-Cache HIT Served from cache
MISS Rendered and cached
UNCACHEABLE Cannot cache (max-age:0)

Auto-Placeholder Conditions

Content is automatically placeholdered when:

  • max-age: 0
  • Cache context user or session
  • #create_placeholder is TRUE

Examples

Example 1: Check Dynamic Cache Status

Input: "How do I know if Dynamic Page Cache is working?"

Output:

# Check response header
curl -sI -H "Cookie: SESS123=abc" https://site.com/ | grep X-Drupal-Dynamic-Cache

# Expected results:
# X-Drupal-Dynamic-Cache: HIT    → Working, served from cache
# X-Drupal-Dynamic-Cache: MISS   → First request, now cached
# X-Drupal-Dynamic-Cache: UNCACHEABLE → Problem! Something has max-age:0

Example 2: Debug UNCACHEABLE Response

Input: "My page shows UNCACHEABLE, how do I fix it?"

Output:

// Enable debug headers to see what's wrong
// settings.local.php
$settings['http.response.debug_cacheability_headers'] = TRUE;

// Check headers:
// X-Drupal-Cache-Max-Age: 0  → Something set max-age:0

// Common culprits:
// 1. Block with max-age:0
// 2. Form without lazy builder
// 3. Custom module not setting cache metadata

// Find the source with Xdebug breakpoint:
// File: Cache.php, Method: mergeMaxAges(), Condition: $a === 0 || $b === 0

Example 3: Lazy Builder for User Content

Input: "I need to show user-specific data without breaking cache"

Output:

// DON'T do this - breaks page caching:
$build['greeting'] = [
  '#markup' => 'Hello ' . $user->getDisplayName(),
  '#cache' => ['contexts' => ['user']], // Page becomes per-user!
];

// DO this - use lazy builder:
$build['greeting'] = [
  '#lazy_builder' => ['my_module.lazy:userGreeting', [$user->id()]],
  '#create_placeholder' => TRUE,
];
// LazyBuilders service
class LazyBuilders implements TrustedCallbackInterface {

  public static function trustedCallbacks() {
    return ['userGreeting'];
  }

  public function userGreeting(int $uid): array {
    $user = User::load($uid);
    return [
      '#markup' => 'Hello ' . $user->getDisplayName(),
      '#cache' => [
        'contexts' => ['user'],
        'tags' => ['user:' . $uid],
      ],
    ];
  }
}

Example 4: BigPipe Streaming

Input: "How does BigPipe work with placeholders?"

Output:

<!-- Initial HTML sent immediately: -->
<div data-big-pipe-placeholder-id="callback=my_module.lazy%3AuserGreeting&args[0]=5">
  <span class="placeholder">Loading...</span>
</div>

<!-- Later, streamed via script tag: -->
<script type="application/vnd.drupal-ajax">
[{"command":"insert",
  "selector":"[data-big-pipe-placeholder-id='callback=my_module.lazy%3AuserGreeting&args[0]=5']",
  "data":"Hello John!"}]
</script>

<!-- JavaScript replaces placeholder content automatically -->

Example 5: Force Placeholder Creation

Input: "I want to placeholder content even without user context"

Output:

$build['dynamic'] = [
  '#lazy_builder' => ['my_module.lazy:dynamicContent', []],
  '#create_placeholder' => TRUE, // Force placeholder regardless of contexts
];

Example 6: Configure Auto-Placeholder Conditions

Input: "How do I customize what gets auto-placeholdered?"

Output:

# services.yml
parameters:
  renderer.config:
    auto_placeholder_conditions:
      max-age: 0
      contexts:
        - 'session'
        - 'user'
        - 'my_custom_context'  # Add custom context
      tags: []  # Tags that trigger placeholder (usually empty)

Example 7: BigPipe Requirements

Input: "BigPipe isn't working, what could be wrong?"

Output:

# Check server configuration:

# Nginx - disable buffering (auto-handled via X-Accel-Buffering header)
location ~ \.php$ {
  # BigPipe sends: X-Accel-Buffering: no
  # Nginx respects this automatically
}

# Apache - works by default

# Common issues:
# 1. Output buffering in PHP - check ob_start() calls
# 2. Reverse proxy buffering - configure Varnish/CDN
# 3. JavaScript disabled - falls back to non-streaming
# Verify BigPipe is enabled
drush pm:list | grep big_pipe

Example 8: Anonymous vs Authenticated Caching

Input: "Explain the caching difference between anonymous and logged-in users"

Output:

Anonymous User:
┌─────────────────────────────────────────┐
│ Page Cache → HIT → Full page served     │
│ (Dynamic Page Cache skipped)            │
│ No placeholders, no BigPipe             │
└─────────────────────────────────────────┘

Authenticated User:
┌─────────────────────────────────────────┐
│ Page Cache → SKIP (has session cookie)  │
│ Dynamic Page Cache → HIT/MISS           │
│ Placeholders replaced via BigPipe       │
└─────────────────────────────────────────┘
// Check with curl:
// Anonymous
curl -sI https://site.com/ | grep X-Drupal
// X-Drupal-Cache: HIT

// Authenticated (with session cookie)
curl -sI -H "Cookie: SESSabc=xyz" https://site.com/ | grep X-Drupal
// X-Drupal-Dynamic-Cache: HIT

Common Mistakes

Mistake Impact Solution
max-age:0 without lazy builder Page UNCACHEABLE Use #lazy_builder
user context on blocks Per-user cache entries Use user.roles or lazy builder
Disabling Dynamic Page Cache Slow authenticated pages Fix underlying max-age issues
Object args to lazy builder Runtime error Use scalar values only

Debugging Checklist

# 1. Check Dynamic Cache status
curl -sI -H "Cookie: SESS=x" https://site.com/ | grep X-Drupal-Dynamic-Cache

# 2. Enable debug headers
# settings.local.php: $settings['http.response.debug_cacheability_headers'] = TRUE;

# 3. Check max-age
curl -sI https://site.com/ | grep X-Drupal-Cache-Max-Age

# 4. Verify BigPipe module
drush pm:list | grep big_pipe