I have a simple block that is used in the footer of a multiple language website. The text in the block changes based on user language so I using the following render attributes, which seems to work well.

$render['#cache'] = [ 'contexts' => ['languages'] ];

This block contains an image that is included with the following:

if( (date('Y') %2) == 1)
  $markup = '<img src="/themes/THEME/images/footer_odd_year.jpg">';
  $markup = '<img src="/themes/THEME/images/footer_even_year.jpg">';

One image is used for even numbered years and a second image is used for odd numbered years.

What is the correct way to setup caching base on this image changing once per year?

I have a similar situation with the home page of the website which changes each day. My current plan is to clear all caching levels for the website early each morning just after mid-night. This includes the Drupal caches, memcached and AWS Cloudfront. For pages like the home page, which change daily, I will need to keep max-age short to limit caching in the user's browser.

Is there any better way?

  • Without knowing the code which changes the image it's hard to tell. - BTW you set the context the wrong way, don't add a new key, cache data in otherwise empty keys are not found in the render process. Add it to contexts in the already existing #cache key: $build['#cache']['contexts'][] (if it seems to work nonetheless, this is because the language context is one of the three default contexts).
    – 4k4
    Nov 19 '17 at 21:35
  • I updated the original post based on your suggestions. Thank you 4k4.
    – Greg Sims
    Nov 20 '17 at 15:39
  • Here is the code for image selection: if( (date('Y') % 2) == 1) use odd year image else use even year image.
    – Greg Sims
    Nov 20 '17 at 15:44

On this time scale, a cache TTL probably isn't the way to go, since the age is counted from creation time without respecting calendar years. You know exactly when the cache should expire (midnight on New Years') so instead of giving it a one-year maximum age it may be better to just actively clean it with a cron.

To avoid throwing away the entire cache (which can lead to significant performance degradation on some sites), you can use a cache tag:

$render['#cache']['tags'] = ['date:year'];

And then, in the annual cron, just invalidate the date:year cache tag like this:

function custom_cron() {
  $last = \Drupal::state()->get('system.cron_last');
  if (date('Y') !== date('Y', $last)) {

(Note that this solution does the absolute minimum of cache invalidation. If you find that your site performs well even if you completely purge the cache every day, that's fine.)


You can probably this using the D8 cache max-age setting. Basically, you can calculate the time until the next year and use this as the max-age. So each time the block is created set the max-cache to be the time until the next year.

To set the max_age, use:

$render['cache']['max_age']['#default_value'] = $time_until_next_year;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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