Fighting client-side caching in Django

DjangoCaching

Django Problem Overview


I'm using the render_to_response shortcut and don't want to craft a specific Response object to add additional headers to prevent client-side caching.

I'd like to have a response that contains:

  • Pragma: no-cache
  • Cache-control : no-cache
  • Cache-control: must-revalidate

And all the other nifty ways that browsers will hopefully interpret as directives to avoid caching.

Is there a no-cache middleware or something similar that can do the trick with minimal code intrusion?

Django Solutions


Solution 1 - Django

You can achieve this using the cache_control decorator. Example from the documentation:

from django.views.decorators.cache import never_cache

@never_cache
def myview(request):
   # ...

Solution 2 - Django

This approach (slight modification of L. De Leo's solution) with a custom middleware has worked well for me as a site wide solution:

from django.utils.cache import add_never_cache_headers

class DisableClientSideCachingMiddleware(object):
    def process_response(self, request, response):
        add_never_cache_headers(response)
        return response

This makes use of add_never_cache_headers.


If you want to combine this with UpdateCacheMiddleware and FetchFromCacheMiddleware, to enable server-side caching while disabling client-side caching, you need to add DisableClientSideCachingMiddleware before everything else, like this:

MIDDLEWARE_CLASSES = (
    'custom.middleware.DisableClientSideCachingMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    # ... all other middleware ...
    'django.middleware.cache.FetchFromCacheMiddleware',
)

Solution 3 - Django

To supplement existing answers. Here is a decorator that adds additional headers to disable caching:

from django.views.decorators.cache import patch_cache_control
from functools import wraps

def never_ever_cache(decorated_function):
    """Like Django @never_cache but sets more valid cache disabling headers.

    @never_cache only sets Cache-Control:max-age=0 which is not
    enough. For example, with max-axe=0 Firefox returns cached results
    of GET calls when it is restarted.
    """
    @wraps(decorated_function)
    def wrapper(*args, **kwargs):
        response = decorated_function(*args, **kwargs)
        patch_cache_control(
            response, no_cache=True, no_store=True, must_revalidate=True,
            max_age=0)
        return response
    return wrapper

And you can use it like:

class SomeView(View):
    @method_decorator(never_ever_cache)
    def get(self, request):
        return HttpResponse('Hello')

Solution 4 - Django

Actually writing my own middleware was easy enough:

from django.http import HttpResponse


class NoCacheMiddleware(object):
    
    def process_response(self, request, response):
        
        response['Pragma'] = 'no-cache'
        response['Cache-Control'] = 'no-cache must-revalidate proxy-revalidate'
        
        return response

Still doesn't really behave like i wanted but so neither does the @never_cache decorator

Solution 5 - Django

I was scratching my head when the three magic meta didn't work in Firefox and Safari.

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />

Apparently it can happen because some browsers will ignore the client side meta, so it should be handled at server side.

I tried all the answers from this post for my class based views (django==1.11.6). But referring to answers from @Lorenzo and @Zags, I decided to write a middleware which I think is a simple one.

So adding to other good answers,

# middleware.py
class DisableBrowserCacheMiddleware(object):

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        response['Pragma'] = 'no-cache'
        response['Cache-Control'] = 'no-cache, no-store, must-revalidate'
        response['Expires'] = '0'
        return response

# settings.py
MIDDLEWARE = [
    'myapp.middleware.DisableBrowserCacheMiddleware',
    ...

Solution 6 - Django

Regarding the Google Chrome browser (Version 34.0.1847.116 m) and the other browsers, I found that only the @cache_control decorator is working. I use Django 1.6.2.

Use it like this:

@cache_control(max_age=0, no_cache=True, no_store=True, must_revalidate=True)
def view(request):
    ...

Solution 7 - Django

Here is a rewrite of @Meilo's answer for Django 1.10+:

from django.utils.cache import add_never_cache_headers

class DisableClientCachingMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        add_never_cache_headers(response)
        return response

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLorenzoView Question on Stackoverflow
Solution 1 - DjangoKristianView Answer on Stackoverflow
Solution 2 - DjangoMeiloView Answer on Stackoverflow
Solution 3 - DjangoJan WrobelView Answer on Stackoverflow
Solution 4 - DjangoLorenzoView Answer on Stackoverflow
Solution 5 - DjangoHussainView Answer on Stackoverflow
Solution 6 - DjangoErwanView Answer on Stackoverflow
Solution 7 - DjangoZagsView Answer on Stackoverflow