Smart ETag and Last-Modified handling with cache-headers plugin

Posted by: on Oct 26, 2010 | 2 Comments

I’ve been working on the next release of Weceem CMS these last few weeks and one of the issues to tackle was that Weceem was not setting any of the useful response headers used by browsers and intermediate proxies/caches.

This obviously needed improvement, and this tied in well to work I had already done on the small but beautiful Grails cache-headers plugin.

The problem: with a CMS you generally can’t make the client cache any content, not without some config for the CMS author/administrator to specify how long to cache it for – this is a problem we will tackle later. However at the very least you want to avoid re-generating content when requested if the client already has the same content.

The solution: Implement ETag and Last-Modified handling correctly so that when you receive requests with If-None-Match or If-Modified-Since you can return “304 Not Modified” if the content is the same as that the client last downloaded. This can give you major improvements in client response time and also server load, even in applications with highly dynamic content.

The thing is, implementing this from scratch in every Grails controller action you have is rather dull.

So in steps the new withCacheHeaders method in the Cache-Headers plugin. This method works a little like Grails’ excellent withFormat method, using a simple DSL to specify:

  • How to generate the ETag for this request
  • How to calculate the Last-Modified date for this request
  • How to render the response, if we need to do this because the content has changed since the client last downloaded it.

Using this information that you provide, the cache-headers plugin will automatically work out if it can return “304 Not Modified” or if it has to render your response for you.

Here’s a working example from Weceem’s content rendering controller:

As you can see there are three “method” calls inside the withCacheHeaders call: etag, lastModified and generate. You simply implement these and the plugin handles the rest.

In the “generate” closure we’re also calling the lastModified method added to controller actions by the cache-headers plugin. This lets us easily set the Last-Modified header of the response, and then we make a token effort at setting client/proxy caching headers with the “cache” method. This will be subject to later refinement.

For more details see the docs.

2 Comments

  1. Rainer Schmitz
    December 8, 2010

    Hi Marc,

    nice work!

    Am I right that the Caching Headers Plugin will only work for GET requests?
    For example, it does not support “If-Match” for PUT requests?

    Regards,
    Rainer

    Reply
    • Marc Palmer
      December 19, 2010

      No, cache-headers does nothing to do with request method. You do this.

      Reply

Leave a Reply