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.
Here’s a decorator solution for per-view caching.
from django.middleware.cache import CacheMiddleware
from django.utils.cache import add_never_cache_headers
from django.utils.decorators import decorator_from_middleware_with_args
class ServerSideOnlyCache(CacheMiddleware):
"""Cache results on the server and instruct the client's browser to not cache."""
def process_request(self, request):
response = super().process_request(request)
if response:
add_never_cache_headers(response)
return response
def process_response(self, request, response):
response = super().process_response(request, response)
add_never_cache_headers(response)
return response
def cache_page_server_side(timeout, *, cache=None, key_prefix=None):
"""Decorator for views that instructs the client to not cache."""
return decorator_from_middleware_with_args(ServerSideOnlyCache)(
page_timeout=timeout,
cache_alias=cache,
key_prefix=key_prefix,
)