drupal-cache-maxage

Max-age defines time-based cache validity in seconds.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "drupal-cache-maxage" with this command: npx skills add sparkfabrik/sf-awesome-copilot/sparkfabrik-sf-awesome-copilot-drupal-cache-maxage

Drupal Cache Max-Age

Max-age defines time-based cache validity in seconds.

When to Use

  • Caching external API data with known refresh intervals

  • Time-sensitive content (countdowns, live scores)

  • Understanding why anonymous pages stay cached

  • Debugging UNCACHEABLE responses

Critical Behavior

Page Cache ignores max-age - Only uses tags for invalidation. Dynamic Page Cache respects max-age - max-age:0 causes UNCACHEABLE. Max-age bubbles up - Minimum value wins across render tree.

Max-Age Values

Value Meaning

0

Uncacheable - DANGER: bubbles up!

Cache::PERMANENT (-1) Forever until tag invalidation

3600

1 hour

86400

1 day

Examples

Example 1: External API Data

Input: "I fetch weather data from an API, how do I cache it for 15 minutes?"

Output:

$build = [ '#markup' => $this->weatherService->getCurrentWeather(), '#cache' => [ 'max-age' => 900, // 15 minutes 'tags' => ['weather:current'], ], ];

Example 2: Cache Forever Until Invalidation

Input: "My content only changes when the entity is edited"

Output:

use Drupal\Core\Cache\Cache;

$build = [ '#markup' => $node->getTitle(), '#cache' => [ 'max-age' => Cache::PERMANENT, // -1, forever 'tags' => $node->getCacheTags(), // Invalidates on node save ], ];

Example 3: Time-Sensitive Countdown

Input: "I show a countdown to an event, how do I handle cache?"

Output:

public function buildCountdown(): array { $event_time = strtotime('2024-12-31 23:59:59'); $remaining = $event_time - time();

return [ '#markup' => $this->formatCountdown($remaining), '#cache' => [ // Cache for 1 minute or until event, whichever is shorter 'max-age' => $remaining > 0 ? min($remaining, 60) : 0, ], ]; }

Example 4: max-age:0 Causes Page UNCACHEABLE

Input: "Why does X-Drupal-Dynamic-Cache show UNCACHEABLE?"

Output:

// PROBLEM: This makes entire page uncacheable $build['timestamp'] = [ '#markup' => time(), '#cache' => ['max-age' => 0], // Bubbles up to page! ];

// SOLUTION: Use lazy builder to isolate uncacheable content $build['timestamp'] = [ '#lazy_builder' => ['my_module.lazy:renderTimestamp', []], '#create_placeholder' => TRUE, ];

Example 5: Page Cache Ignores max-age

Input: "I set max-age to 300 but anonymous users still see old content after 5 minutes"

Output:

// max-age:300 does NOT work for Page Cache! // Page Cache only respects cache tags, not max-age

// Option 1: Use cache tags and invalidate when data changes Cache::invalidateTags(['my_module:data']);

// Option 2: Disable Page Cache for this route // In your_module.routing.yml: my_module.dynamic_page: path: '/dynamic' defaults: _controller: '\Drupal\my_module\Controller::page' options: no_cache: TRUE

// Option 3: Kill Page Cache programmatically \Drupal::service('page_cache_kill_switch')->trigger();

Example 6: Finding max-age:0 Source

Input: "How do I find what's setting max-age to 0?"

Output:

// Method 1: Xdebug conditional breakpoint // File: core/lib/Drupal/Core/Cache/Cache.php // Method: mergeMaxAges() // Condition: $a === 0 || $b === 0

// Method 2: Temporary debug logging // Add to Cache::mergeMaxAges() temporarily: if ($a === 0 || $b === 0) { \Drupal::logger('cache_debug')->warning( 'max-age:0 at: @trace', ['@trace' => json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5))] ); }

Example 7: Browser Cache-Control Headers

Input: "How do I control browser caching separately from Drupal cache?"

Output:

// Site-wide: Admin > Config > Development > Performance // Sets Cache-Control header for anonymous pages

// Per-response in controller: use Symfony\Component\HttpFoundation\Response;

public function myPage(): Response { $build = ['#markup' => 'Content']; $html = \Drupal::service('renderer')->renderRoot($build);

$response = new Response($html); $response->headers->set('Cache-Control', 'public, max-age=3600');

return $response; }

Max-Age Bubbling Behavior

// Parent: max-age 3600 $build = [ '#markup' => 'Parent', '#cache' => ['max-age' => 3600], ];

// Child: max-age 0 $build['child'] = [ '#markup' => 'Child', '#cache' => ['max-age' => 0], ];

// Result: entire $build has effective max-age: 0 // The minimum always wins!

Common Mistakes

Mistake Impact Solution

max-age:0 in render array Entire page uncacheable Use lazy builder

Relying on max-age for Page Cache Pages never expire Use cache tags + invalidation

Short max-age on stable content Unnecessary re-renders Use tags, set PERMANENT

Forgetting bubbling Child max-age:0 breaks parent Audit all render elements

Debugging

Check max-age header

curl -sI https://site.com/ | grep -i 'cache-control|x-drupal-cache-max-age'

Clear render cache and test

drush cache:clear render curl -sI https://site.com/node/1

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

drupal-lazy-builders

No summary provided by upstream source.

Repository SourceNeeds Review
General

drupal-cache-debugging

No summary provided by upstream source.

Repository SourceNeeds Review
General

drupal-cache-tags

No summary provided by upstream source.

Repository SourceNeeds Review