diff --git a/django/utils/cache.py b/django/utils/cache.py index e48e159910f..66d5e75e407 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -134,6 +134,7 @@ def add_never_cache_headers(response): Adds headers to a response to indicate that a page should never be cached. """ patch_response_headers(response, cache_timeout=-1) + patch_cache_control(response, no_cache=True, no_store=True, must_revalidate=True) def patch_vary_headers(response, newheaders): diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index d6a5312e738..85fc4cb6ff9 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -1474,7 +1474,7 @@ templates used by the :class:`ModelAdmin` views: url(r'^my_view/$', self.admin_site.admin_view(self.my_view)) This wrapping will protect ``self.my_view`` from unauthorized access and - will apply the ``django.views.decorators.cache.never_cache`` decorator to + will apply the :func:`django.views.decorators.cache.never_cache` decorator to make sure it is not cached if the cache middleware is active. If the page is cacheable, but you still want the permission check to be diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 18312957fc5..7912edd5ce5 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -65,7 +65,13 @@ need to distinguish caches by the ``Accept-language`` header. .. function:: add_never_cache_headers(response) - Adds headers to a response to indicate that a page should never be cached. + Adds a ``Cache-Control: max-age=0, no-cache, no-store, must-revalidate`` + header to a response to indicate that a page should never be cached. + + .. versionchanged:: 1.9 + + Before Django 1.9, ``Cache-Control: max-age=0`` was sent. This didn't + reliably prevent caching in all browsers. .. function:: patch_vary_headers(response, newheaders) diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index 21df59f051e..cceb0611b55 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -118,6 +118,10 @@ Cache * ``django.core.cache.backends.base.BaseCache`` now has a ``get_or_set()`` method. +* :func:`django.views.decorators.cache.never_cache` now sends more persuasive + headers (added ``no-cache, no-store, must-revalidate`` to ``Cache-Control``) + to better prevent caching. + Email ^^^^^ diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt index ee8c30cfefd..109bcc41ff5 100644 --- a/docs/topics/cache.txt +++ b/docs/topics/cache.txt @@ -1209,7 +1209,7 @@ the value of the :setting:`CACHE_MIDDLEWARE_SECONDS` setting. If you use a custo precedence, and the header values will be merged correctly.) If you want to use headers to disable caching altogether, -``django.views.decorators.cache.never_cache`` is a view decorator that adds +:func:`django.views.decorators.cache.never_cache` is a view decorator that adds headers to ensure the response won't be cached by browsers or other caches. Example:: diff --git a/docs/topics/http/decorators.txt b/docs/topics/http/decorators.txt index 25616a44c01..a310576fd5e 100644 --- a/docs/topics/http/decorators.txt +++ b/docs/topics/http/decorators.txt @@ -97,3 +97,22 @@ caching based on specific request headers. into account when building its cache key. See :ref:`using vary headers `. + +.. module:: django.views.decorators.cache + +Caching +======= + +The decorators in :mod:`django.views.decorators.cache` control server and +client-side caching. + +.. function:: never_cache(view_func) + + This decorator adds a ``Cache-Control: max-age=0, no-cache, no-store, + must-revalidate`` header to a response to indicate that a page should never + be cached. + + .. versionchanged:: 1.9 + + Before Django 1.9, ``Cache-Control: max-age=0`` was sent. This didn't + reliably prevent caching in all browsers. diff --git a/tests/decorators/tests.py b/tests/decorators/tests.py index d3e2eddf8c1..d6b2c780685 100644 --- a/tests/decorators/tests.py +++ b/tests/decorators/tests.py @@ -317,3 +317,15 @@ class XFrameOptionsDecoratorsTests(TestCase): # the middleware's functionality, let's make sure it actually works... r = XFrameOptionsMiddleware().process_response(req, resp) self.assertEqual(r.get('X-Frame-Options', None), None) + + +class NeverCacheDecoratorTest(TestCase): + def test_never_cache_decorator(self): + @never_cache + def a_view(request): + return HttpResponse() + r = a_view(HttpRequest()) + self.assertEqual( + set(r['Cache-Control'].split(', ')), + {'max-age=0', 'no-cache', 'no-store', 'must-revalidate'}, + )