I've got a node which cache should expire every day at midnight. Additionally I've a block showing the same node in another display mode, that also should expire every day at midnight. I thought this would be pretty easy by calculating max-age

page_cache is already disabled - dynamic_page_cache is enabled

.module file

function wt_lunch_preprocess_node(array &$variables) {
  $node = $variables['node'];
  if ($node->bundle() == 'wt_lunch') {
    $maxAge = strtotime('tomorrow') - strtotime('now');
    $variables['#cache']['max-age'] = $maxAge;
    $today = strtolower(date('l'));
    $variables['max_age'] = $maxAge; // this line is just for debugging


  public function build() {
    $maxAge = strtotime('tomorrow') - strtotime('now');
    $emptyResult = [
      '#markup' => '',
      '#cache' => ['max-age' => $maxAge]

    $node = \Drupal::entityTypeManager()
      ->loadByProperties(['type' => 'wt_lunch']);

    if (!$node = reset($node)) {
      return $emptyResult;

    $build = \Drupal::entityTypeManager()->getViewBuilder('node')->view($node, 'special');
    $build['#cache']['max-age'] = $maxAge;
    $build['#attached']['library'][] = 'wt_lunch/daily';
    $build['#attached']['drupalSettings']['wt']['lunch']['timer'] = $node->get('field_lunch_daily_timer')->value;
    $build['#attached']['drupalSettings']['wt']['lunch']['from'] = $node->get('field_lunch_daily_from')->value;
    $build['#attached']['drupalSettings']['wt']['lunch']['until'] = $node->get('field_lunch_daily_until')->value;
    $build['#attributes']['class'][] = 'lunch--daily';
    return $build;

This works almost every day. Almost. Every 10-15 days the cache does not expire properly and keeps rendering the block from yesterday.

Today the block didn't expire correctly again. I've previously added hidden debug comments in my Twig template, showing cache infos in the rendered HTML:

{{ date().timestamp }} ==> 1543194778 (roundabout 1am yesterday)
{{ max_age }} ==> 78422 (see $maxAge in code above, about 23 hours)

So I can verify that $build['#cache']['max-age'] definitly was correctly calculated, yet I did get the outdated block today at 11am.

Can anybody give me a clue why the max-age of the block sometimes does not work?

  • Are you sure, that there is no other caching between you and your server (Browser, Proxy etc.)? Maybe you should test with wget. Commented Nov 27, 2018 at 14:13
  • Not tested, but quite certain not a proxy or browser cache issue, because the problem occurs simultaneously on different devices on different locations.
    – Hudri
    Commented Nov 28, 2018 at 8:08

1 Answer 1


I believe your flaw is that if there is day where no one views a node of bundle wt_lunch your nodes max age will get out of alignment; thus, your block will also be displaying an outdated node.

For this, don't use preprocess_node, use Job Scheduler or hook_cron instead.

You might also want to utilize the above with Ultimate Cron.

  • 1
    I would agree, for this case you want something independent of someone having to come view a node.
    – Kevin
    Commented Nov 27, 2018 at 14:19
  • @No Sssweat: To understand my mistake: When I do \Drupal::entityTypeManager()->getViewBuilder('node')->view($node, 'special'); this does NOT trigger mymodule_preprocess_node() and so cache['#max-age'] is not set on the node entity ?
    – Hudri
    Commented Nov 27, 2018 at 14:36
  • @Hudri that is probably it, add \Drupal::logger('my_module')->error('it ran'); to preprocess_node and then run \Drupal::entityTypeManager()->getViewBuilder('node')->view($node, 'special'); in Devel Execute PHP and then check the error log to see if it got recorded.
    – No Sssweat
    Commented Nov 27, 2018 at 14:42
  • 1
    I would also use cron, not to set cache max-age, but to invalidate a cache tag you've added in preprocess, so you can enable the internal page cache again. Wouldn't be a good idea to disable it only to solve this issue for one node.
    – 4uk4
    Commented Nov 27, 2018 at 20:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.