[Django]-Fighting client-side caching in Django

103👍

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):
   # ...

56👍

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',
)
👤Meilo

17👍

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')

11👍

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',
    ...

8👍

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

7👍

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):
    ...
👤Erwan

6👍

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
👤Zags

0👍

For Django 4+:

from django.utils.cache import add_never_cache_headers


def disable_client_side_caching_middleware(get_response):
    def middleware(request):
        response = get_response(request)
        add_never_cache_headers(response)
        return response

    return middleware
MIDDLEWARE_CLASSES = (
    ...
    'my_app.middleware.disable_client_side_caching_middleware'
    ...
)

0👍

For anyone with newer version of Django 4.0+

class DisableClientSideCachingMiddleware:
    """
    Middleware to disable client side caching, that is on browser.
    Adds a Cache-Control: max-age=0, no-cache, no-store, must-revalidate, private header ,
    to a response to indicate that a page should never be cached.

    If not added and the api returns a cached response, the browser also caches the data into disk cache.
    Meaning we don't want caching to happen at browser level on client side as clinent will see data from
    disk cache even if it is changed on server side.

    """

    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


Then add this middleware in your settings,

MIDDLEWARE_CLASSES = (
    ...
    'your_app.middleware.DisableClientSideCachingMiddleware'
    ...
)

Leave a comment