12

It's well known that Internet Explorer aggressively caches ajax calls whereas all the other browsers grab the data fresh every time. This is usually bad: I've never encountered a case where I want ajax to NOT contact the server. Firefox, Safari and the other browsers know this and don't cache ajax calls.

To prevent IE from caching, you have to do one of the following:

  • add a cache-busting token to the query string (like ?time=[timestamp])
  • send a HTTP response header that specifically forbids IE to cache the request
  • use an ajax POST instead of a GET

I much prefer setting a no-cache header. It's the correct way: it tells all browsers not to cache, which is exactly what you intend. The query string method fills up the browser's cache with stuff that'll never be retrieved, leaving less room for legitimate cache content. And the POST method is a corruption of HTTP: POSTs are for modifying data.

In Grails, what's the best way to automatically send a do-not-cache header for all ajax requests? I don't want to modify any controllers, so I'm thinking there's got to be a cool filter trick or something.

Thanks!

Dean Moses
  • 2,372
  • 2
  • 24
  • 36

2 Answers2

19

Here's what I finally figured out. Most javascript libraries --including jQuery, YUI, Mootools and Prototype -- send the X-Requested-With: XmlHttpRequest header on every ajax request.

For any request that sends this header, you can send a response header back that tells it to not cache.

Below is a Grails filter that prevents caching of ajax requests that identify themselves with the X-Requested-With: XmlHttpRequest header:

// put this class in grails-app/config/
class AjaxFilters {
    def filters = {
        all(controller:'*', action:'*') {
            before = {
                if (request.getHeader('X-Requested-With')?.equals('XMLHttpRequest')) {
                    response.setHeader('Expires', '-1')
                }
            }
        }
    }
}

Some people prefer to use the Cache-Control: no-cache header instead of expires. Here's the difference:

  • Cache-Control: no-cache - absolutely NO caching
  • Expires: -1 - the browser "usually" contacts the Web server for updates to that page via a conditional If-Modified-Since request. However, the page remains in the disk cache and is used in appropriate situations without contacting the remote Web server, such as when the BACK and FORWARD buttons are used to access the navigation history or when the browser is in offline mode.

By adding this filter, you make Internet Explorer's caching consistent with what Firefox and Safari already do.

BTW, I've experienced the caching problem on IE8 and IE9. I assume the problem existed for IE7 and IE6 as well.

Dean Moses
  • 2,372
  • 2
  • 24
  • 36
2

We use jQuery for all ajax calls so we add this block to our main.gsp (top-level layout):

<g:javascript>
  jQuery(document).ready(function() {
    $.ajaxSetup({
      cache:false
    });
  });
</g:javascript>

Also answered here

Community
  • 1
  • 1
David Betts
  • 776
  • 4
  • 7
  • what if the webpage making the AJAX call lives in another website ? example : you serve an API... – Grooveek May 13 '11 at 22:01
  • Cool, thanks @david-betts. We're using YUI but I just looked and it has the same functionality. I'll use this if I have to... however I'd rather control this on the server because adding stuff to the URL can have side effects. If no one gives a server-side answer by tomorrow I'll accept this one. – Dean Moses May 13 '11 at 22:11
  • If you make the ajax call to any system through jQuery after doing this setup, my expectation would be that it would not be cached as it appends a timestamp to every request. – David Betts May 13 '11 at 22:12
  • Is there someway you could detect ajax calls using url mapping or detecting JSON content type? If so then you could add a filter that does this: `response.setHeader "Cache-Control", "no-cache"` – David Betts May 13 '11 at 22:15
  • @david-betts Right, I'm thinking I can set up some sort of post-filter in the Grails filters that sets the caching header, but I'm not quite sure, since at that point the response is probably already committed. Rather than exploring myself I had hoped someone would have already done it... – Dean Moses May 13 '11 at 22:26
  • Also, reading more on YUI's cache-busting, I just realized I can use POST for all my ajax calls instead of GET. POSTs are never cached. – Dean Moses May 13 '11 at 22:28