Bypassing the Varnish cache

Varnish

While it's very important for your site to use a Varnish cache to perform and scale well, when you have a few pages with random or dynamic content it can be a valid business requirement to selectively exclude certain paths from being cached so that dynamic content is actually dynamic. The most obvious way to achieve this would be to alter the Varnish Configuration Language (VCL) file. However, this may seem excessive. Not only does it require dedicated load balancers on Acquia Cloud, but this is something Drupal can handle very well at the application layer.

Modules

With Pressflow in Drupal 6, the Cookie Cache Bypass module was very useful because it allowed you to set a short-lived NO_CACHE cookie on form submissions. You could set it so that when you are using a reverse proxy, such as Varnish, you could be certain that your site visitors would get dynamic pages after form submissions. Drupal 7 doesn't ship with this module, but you can use the Cookie Cache Bypass Advanced module. It does almost the same thing, but also gives you — among other things — control over the maximum lifetime setting for the NO_CACHE cookie. This is very handy.

As with many things in Drupal, it's usually better to keep the process simple and sustainable by using a Drupal module. The CacheExclude module is preferred because it's straightforward and works with little need for configuration. You just need to define one or several paths that you want to exclude from caching, or you can completely exclude a content type if needed. As always, be aware of performance issues that might arise when you bypass caching for heavy traffic pages or content types, because it will force Drupal to completely rebuild those pages each time.

This image shows the cache exclude settings screen.

cacheexclude_settings-1.png

It's an extreme case, but if you're already using the Context module, you might be interested in evaluating the add-on module, Context HTTP Headers. It provides for HTTP response codes altering directly from the Context reaction section UI. It exposes two hooks (hook_context_http_header_def() and hook_context_http_header_build()) for developers to implement some additional logic. Read the module's README file for more information.

Changing HTTP headers

You may be aware that SESS cookies will always bypass Varnish, and as a result, that's why you're not getting Varnish HITS when you're logged in. For anonymous users, though, you can alter the HTTP headers of a page by using the Drupal API. A module for Drupal 7 and 8, Advanced Page Expiration, can handle this on a path-by-path basis. If you want more control over this, you can use a custom module.

By setting the Cache-Control max-age header to 0 in a custom module, which is equivalent to not caching the page at all, you'll bypass entirely the Drupal caching mechanisms. To do that with Drupal 6, you need to use the drupal_set_header() function:

drupal_set_header('Cache-Control', 'public, max-age=0');

With Drupal 7, the drupal_set_header() function has been deprecated, so you'll have to use drupal_add_http_header() instead:

drupal_add_http_header('Cache-Control', 'public, max-age=0');

You could also simply add the following code in the settings.php file, and it would immediately disable caching. The problem is that this is certainly not something you'd like to do since it would affect all pages. This would actually be similar to disabling caching completely from the Drupal backend. We do not recommend this because of performance concerns. The only implementation you could then use is to wrap the following code in an if statement:

$conf['cache'] = '0';

You can disable external page caching for the current request by overriding Drupal's variables. You can do this by adding code to a custom module similar to this:

if ($_SERVER['SCRIPT_URL'] == '/path/to/page') {  $GLOBALS['conf']['cache'] = CACHE_DISABLE;}

Alternatively, instead of disabling cache, you can specify a shorter cache lifetime so that the result of this request expires sooner in Varnish:

if ($_SERVER['SCRIPT_URL'] == '/path/to/page') {  // Set this page to only be cached externally for 30 seconds.  $GLOBALS['conf']['page_cache_maximum_age'] = 30;}

Drupal's Suppressing caching (for development) documentation page offers another solution that hasn't been tested thoroughly. We've provided a link to it for information only.

Whatever scenario you prefer, you'll always have to make sure you're not disabling caches for all pages. You'll also have to carefully evaluate the best method to implement your custom code so that it applies specifically to the pages you don't want to be cached.

Blocks

Blocks may not always clear from the cache, even if you have forced a cache clear of a page the block appears on. The nature of blocks allows them to appear on multiple pages; if you only clear one page that the cached block appears on, the cached version may remain for other pages.

There are ways to force Drupal to bootstrap and clear the block cache for a particular block, but it can severely degrade your website performance. In many cases, it's safer to occasionally clear the cache for all blocks, rather than to try to force a few specific ones.

Contact supportStill need assistance? Contact Acquia Support