Using the Vary header with the @cache_page decorator

February 27, 2015   

Problem

Earlier today I needed to cache a view with the @cache_page decorator. The output of this view changes depending on whether the user making the request is logged in or not. Pretty standard stuff.

However, I quickly discovered that @cache_page was caching both authenticated and anonymous requests under the same cache key. This led to the cache serving either the authenticated or anonymous version of the page, whichever was cached first. Not good.

I eventually found this blog post and this Django ticket and between the two figured it out.

Solution

In short, @cache_page created the same cache key for both authenticated and anonymous versions because a Vary header hadn’t been set yet on the response. If the Vary: Cookie HTTP header had been set, the cache key would have changed depending on the value of the Cookie header and everything would have worked just fine.

The cleanest fix I could find is to use two decorators. First is to wrap the real view function with @vary_on_cookie (to set the Vary: Cookie header) and then wrap that function with @cache_page (to actually cache the page). Like so:

<code>@cache_page(60*60)
@vary_on_cookie
def expensive_func(request):
    # ...
</code>

After doing this, everything worked.