Disabling Drupal 7 cache on specific pages

Posted on 28/01/2016

There are certain cases in which caching is not desirable because it can cause conflicts with the way certain pages work.

The best example is Captcha module, which automatically disables the page/block cache wherever it is displayed. This is necessary because Captcha output should never be cached. The code has to be regenerated for every visitor, in order to be unique and prevent bots from automatic submissions.

In certain cases you will not want your pages to be cached as well. Here's a simplified example: let's say that you created a page through a custom module that is accessible to anonymous users. Before the content is rendered, the page needs to write something to the database. In our example, we will simply log a message to the {watchdog} table.

Here's how your code in sample.module would look like:

  1. /**
  2.  * Implements hook_menu().
  3.  */
  4. function sample_menu() {
  5.   $items['sample'] = array(
  6.     'title' => 'Sample page',
  7.     'page callback' => 'sample_page_callback',
  8.     'access callback' => TRUE,
  9.   );
  10.  
  11.   return $items;
  12. }
  13.  
  14. /**
  15.  * Sample page callback.
  16.  */
  17. function sample_page_callback() {
  18.   // Log the visit.
  19.   watchdog('sample', 'Page loaded by someone at @ip.', array('@ip' => ip_address()));
  20.   // Show the output.
  21.   return 'Hello, World!';
  22. }

If the core page cache for anonymous users is turned off, this example will work for any visitor.

However, if page caching is on, the output will be stored in the database and your code will actually not be run upon every visit. This means that the call to watchdog() function above will be executed only when the page cache is regenerated.

To fix this, you need to disable cache on this page. Disabling the cache site-wide is not the best decision, as you can still benefit from it on other pages.

There are two ways of disabling the cache on a particular page:

  1. Installing the Cache Exclude module.
  2. Doing this through a custom module.

If you already include a custom module in your projects (which you should), achieving this functionality is trivial. You should avoid adding more modules to a site whenever possible, especially if you need them only for one feature.

Here's how you can do this through a custom module:

  1. Add a hook_init() implementation.
  2. See if the user is on the page where you want to disable caching.
  3. Call the drupal_page_is_cacheable() function, passing FALSE as argument. You can read about this function here.

Here's the actual code you would use:

  1. /**
  2.  * Implements hook_init().
  3.  */
  4. function sample_init() {
  5.   if (current_path() == 'sample') {
  6.     drupal_page_is_cacheable(FALSE);
  7.   }
  8. }

That's it. You just need to clear all caches so that the hook_init() implementation is picked up by the system.

Update: Anthony Robertson pointed out a very handy thing - the drupal_page_is_cacheable() can be called from a page callback, meaning that if you define the callback yourself, there is no need for adding hook_init() - you can just call the function yourself and it will prevent the caching. Here's an example:

  1. /**
  2.  * Implements hook_menu().
  3.  */
  4. function sample_menu() {
  5.   // Yes, this is not PHP 7 syntax, the article is for D7 and not all contrib
  6.   // modules are fully compatible with PHP 7.
  7.   $items = array();
  8.   $items['sample'] = array(
  9.     'page callback' => 'sample_page_callback',
  10.     'access callback' => TRUE,
  11.   );
  12. }
  13.  
  14. /**
  15.  * Renders our sample page.
  16.  */
  17. function sample_page_callback() {
  18.   // Prevent the page from being cached.
  19.   drupal_page_is_cacheable(FALSE);
  20.   // Render the page as usual.
  21.   return t('Hello! This page is not cached. Current timestamp: @timestamp.', array('@timestamp' => REQUEST_TIME));
  22. }

Further reading: