server caching without browser caching - http headers

I am trying to configure Django so I can have server caching without browser caching (I am using a serviceworker, and the browser caching is interfering when I am fetching data from server, especially on ios).

When I configure caching in Django, using:

# settings.py
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "unique-snowflake"
    }
}

and

# urls.py
from django.views.decorators.cache import cache_page
urlpatterns = [
    path('', cache_page(6000)(views.HomePage.as_view()), name='home')
]

It also changes the http-header to {cache-control: 6000}, which I don’t want. I would like to have {cache-control: no-cache or no-store} sent as http header.

I tried to use an additional decorator in my class based-view:

from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache

@method_decorator(never_cache, name='dispatch')
class HomePage(TemplateView):
    template_name = 'index.html'

In this case, my http-header is correctly setup and browser caching is disallow. But server caching is not working anymore.

I don’t get it. As far as I understand, server caching and browser caching are two different things, it should be possible to configure them separately.

Is it possible? And if yes, how do you it?

Thanks in advance guys

I have not tried to handle this case, but I suspect what’s happening is that never_cache is adding the headers to tell downstream caches to not cache, but then you wrap never_cache with cache_page in your urls which re-adds that header back. If that is the case, then a potential solution would be:

# urls.py
urlpatterns = [
    path('', views.HomePage.as_view(), name='home')
]

# views.py
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page, never_cache

@method_decorator(never_cache, name='dispatch')
@method_decorator(cache_page(60000), name='dispatch')
class HomePage(TemplateView):
    template_name = 'index.html'

This will cache the view, but then remove the headers for downstream caches after that due to the order of the decorators.

1 Like

Thank you for your answer. I finally found another method which is the following. I created a custom middleware:

from django.utils.cache import add_never_cache_headers

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

Then, I put this middleware at the top of the middleware list:

MIDDLEWARE = [
    'immoboost.middleware.DisableClientSideCachingMiddleware',
    # other middlewares
]

This gives me the following http header for all my responses:

Cache-Control: max-age=0, no-cache, no-store, must-revalidate, private

It’s useful in my case since I want the cache to be performed by the service worker only, without any interference with the browser cache.

I’ll posit that historic naming may be unfortunate there—it’s the crux of a frequent misunderstanding that cache_page() decorator is dual-purpose (it caches page response, and adds headers), while never_cache() only handles headers.

In spirit they’re supposed to be mutually exclusive most of the time, but in some scenarios (especially during active development) never_cache(cache_page(...)) could be (very counter-intuitively) composed.