drupal-cache-debugging

📁 sparkfabrik/sf-awesome-copilot 📅 Jan 26, 2026
4
总安装量
4
周安装量
#48221
全站排名
安装命令
npx skills add https://github.com/sparkfabrik/sf-awesome-copilot --skill drupal-cache-debugging

Agent 安装分布

claude-code 4
codex 4
cursor 4
opencode 3
gemini-cli 3
antigravity 3

Skill 文档

Drupal Cache Debugging

Systematic approaches to identify and resolve caching issues.

When to Use

  • Pages not caching for anonymous users
  • X-Drupal-Dynamic-Cache shows UNCACHEABLE
  • Content is stale after edits
  • Need to find source of max-age:0
  • Performance profiling cache effectiveness

Debug Headers Setup

// settings.local.php
$settings['http.response.debug_cacheability_headers'] = TRUE;

Warning: Disable in production – headers can exceed 16KB.

Headers Reference

Header Values Meaning
X-Drupal-Cache HIT/MISS Page Cache (anonymous)
X-Drupal-Dynamic-Cache HIT/MISS/UNCACHEABLE Dynamic Page Cache
X-Drupal-Cache-Tags Space-separated tags All cache tags
X-Drupal-Cache-Contexts Space-separated contexts Applied contexts
X-Drupal-Cache-Max-Age Seconds or -1 Effective max-age

Examples

Example 1: Quick Health Check

Input: “How do I quickly check if caching is working?”

Output:

# Anonymous user - should hit Page Cache
curl -sI https://site.com/ | grep -iE 'x-drupal|cache-control'

# Expected output:
# X-Drupal-Cache: HIT
# Cache-Control: max-age=3600, public

# Authenticated user - should hit Dynamic Cache
curl -sI -H "Cookie: SESSabc=xyz" https://site.com/ | grep X-Drupal-Dynamic-Cache

# Expected output:
# X-Drupal-Dynamic-Cache: HIT

Example 2: Debug Page Not Caching (Anonymous)

Input: “Anonymous pages always show X-Drupal-Cache: MISS”

Output:

# Step 1: Check for session cookies being set
curl -sI https://site.com/ | grep -i set-cookie
# If you see SESS* or SSESS* → Something creates session for anonymous

# Step 2: Check if Page Cache module is enabled
drush pm:list | grep page_cache

# Step 3: Search for kill switch usage
grep -r "page_cache_kill_switch" web/modules/custom/

# Step 4: Check for session-creating code
grep -r "getSession\|session_start\|\$_SESSION" web/modules/custom/

Example 3: Debug UNCACHEABLE Response

Input: “X-Drupal-Dynamic-Cache shows UNCACHEABLE, how do I fix it?”

Output:

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

# Step 2: Check max-age
curl -sI https://site.com/ | grep X-Drupal-Cache-Max-Age
# If 0 → Something set max-age:0

# Step 3: Find the source with Xdebug
# Set conditional breakpoint in core/lib/Drupal/Core/Cache/Cache.php
# Method: mergeMaxAges()
# Condition: $a === 0 || $b === 0
# Check stack trace when it hits

Example 4: Debug Stale Content After Edit

Input: “I edited a node but old content still shows”

Output:

# Step 1: Clear caches and verify still stale
drush cr
curl -sI https://site.com/node/1 | grep X-Drupal-Cache
# If still HIT with old content → Tags not invalidating

# Step 2: Check cache tags on response
curl -sI https://site.com/node/1 | grep X-Drupal-Cache-Tags
# Should include: node:1

# Step 3: Manually invalidate and test
drush cache-tag-invalidate node:1
curl https://site.com/node/1 | grep "expected new content"

# Step 4: If using Varnish/CDN, check purge queue
drush p:queue-stats

Example 5: Find max-age:0 Source with Temporary Logging

Input: “I can’t use Xdebug, how else can I find max-age:0?”

Output:

// Temporarily modify core/lib/Drupal/Core/Cache/Cache.php
// Add to mergeMaxAges() method:

public static function mergeMaxAges($a, $b) {
  // TEMPORARY DEBUG - REMOVE AFTER FINDING ISSUE
  if ($a === 0 || $b === 0) {
    $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
    $simplified = array_map(function($t) {
      return ($t['class'] ?? '') . '::' . ($t['function'] ?? '') . ' in ' . ($t['file'] ?? '');
    }, $trace);
    \Drupal::logger('cache_debug')->warning('max-age:0 merge: @trace', [
      '@trace' => implode("\n", $simplified),
    ]);
  }
  // END TEMPORARY DEBUG
  
  return min($a, $b);
}

// Then check logs:
// drush watchdog:show --type=cache_debug

Example 6: Enable Render Debug Comments

Input: “How do I see cache info per render element in HTML?”

Output:

# development.services.yml
parameters:
  twig.config:
    debug: true
  renderer.config:
    debug: true
<!-- HTML output will include: -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'node' -->
<!-- FILE NAME SUGGESTIONS: ... -->
<!-- CACHE TAGS: node:1 node_view -->
<!-- CACHE CONTEXTS: user.permissions languages -->
<!-- CACHE MAX-AGE: -1 -->

Example 7: WebProfiler Analysis

Input: “How do I use WebProfiler for cache debugging?”

Output:

# Install Devel and WebProfiler
composer require drupal/devel
drush en devel webprofiler

# Access the profiler toolbar at bottom of page (admin users)
# Click "Cache" panel to see:
# - Cache hit/miss counts per bin
# - All collected cache tags
# - Applied cache contexts
# - Timeline of cache operations

Example 8: Database Cache Inspection

Input: “How do I inspect cache entries directly in database?”

Output:

-- Check render cache entries for a node
SELECT cid, expire, tags 
FROM cache_render 
WHERE cid LIKE '%node%' 
LIMIT 10;

-- Find entries with specific tag
SELECT cid, created, expire 
FROM cache_render 
WHERE tags LIKE '%node:123%';

-- Count entries per cache bin
SELECT 'cache_render' as bin, COUNT(*) as entries FROM cache_render
UNION ALL
SELECT 'cache_page', COUNT(*) FROM cache_page
UNION ALL
SELECT 'cache_dynamic_page_cache', COUNT(*) FROM cache_dynamic_page_cache;

Example 9: Drush Cache Commands

Input: “What drush commands help with cache debugging?”

Output:

# Full cache rebuild
drush cr

# Clear specific bin
drush cache:clear render
drush cache:clear page
drush cache:clear dynamic_page_cache
drush cache:clear discovery

# Invalidate specific tag
drush cache-tag-invalidate node:1
drush cache-tag-invalidate "config:system.site"

# Get cache item programmatically
drush php:eval "print_r(\Drupal::cache('render')->get('entity_view:node:1:full'));"

# List all cache bins
drush php:eval "print_r(array_keys(\Drupal::getContainer()->getParameter('cache_bins')));"

Debugging Decision Tree

Page not caching?
├── Anonymous user?
│   ├── X-Drupal-Cache: MISS always?
│   │   └── Check for session cookies, kill switch
│   └── X-Drupal-Cache: HIT but stale?
│       └── Check cache tags, invalidation
└── Authenticated user?
    ├── X-Drupal-Dynamic-Cache: UNCACHEABLE?
    │   └── Find max-age:0 source
    ├── X-Drupal-Dynamic-Cache: MISS always?
    │   └── Check if module enabled, cache bin working
    └── Dynamic Cache working but slow?
        └── Check for missing lazy builders on personalized content

Common Issues Quick Reference

Symptom Likely Cause First Check
Always MISS (anonymous) Session created curl -I for Set-Cookie
Always UNCACHEABLE max-age:0 X-Drupal-Cache-Max-Age header
Stale after edit Missing tags X-Drupal-Cache-Tags header
Per-user cache explosion user context X-Drupal-Cache-Contexts header
BigPipe not streaming Server buffering Check Nginx/Apache config