diff --git a/django/utils/cache.py b/django/utils/cache.py index 8d0ce4dac5..0f6232b51b 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -122,14 +122,21 @@ def _precondition_failed(request): def _not_modified(request, response=None): + new_response = HttpResponseNotModified() if response: - # We need to keep the cookies, see ticket #4994. - cookies = response.cookies - response = HttpResponseNotModified() - response.cookies = cookies - return response - else: - return HttpResponseNotModified() + # Preserve the headers required by Section 4.1 of RFC 7232, as well as + # Last-Modified. + for header in ('Cache-Control', 'Content-Location', 'Date', 'ETag', 'Expires', 'Last-Modified', 'Vary'): + if header in response: + new_response[header] = response[header] + + # Preserve cookies as per the cookie specification: "If a proxy server + # receives a response which contains a Set-cookie header, it should + # propagate the Set-cookie header to the client, regardless of whether + # the response was 304 (Not Modified) or 200 (OK). + # https://curl.haxx.se/rfc/cookie_spec.html + new_response.cookies = response.cookies + return new_response def get_conditional_response(request, etag=None, last_modified=None, response=None): diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py index 7bf53cfdeb..a9cb1f31ea 100644 --- a/tests/middleware/tests.py +++ b/tests/middleware/tests.py @@ -614,6 +614,28 @@ class ConditionalGetMiddlewareTest(SimpleTestCase): self.resp = ConditionalGetMiddleware().process_response(self.req, self.resp) self.assertEqual(self.resp.status_code, 400) + def test_not_modified_headers(self): + """ + The 304 Not Modified response should include only the headers required + by section 4.1 of RFC 7232, Last-Modified, and the cookies. + """ + self.req.META['HTTP_IF_NONE_MATCH'] = self.resp['ETag'] = '"spam"' + self.resp['Date'] = 'Sat, 12 Feb 2011 17:35:44 GMT' + self.resp['Last-Modified'] = 'Sat, 12 Feb 2011 17:35:44 GMT' + self.resp['Expires'] = 'Sun, 13 Feb 2011 17:35:44 GMT' + self.resp['Vary'] = 'Cookie' + self.resp['Cache-Control'] = 'public' + self.resp['Content-Location'] = '/alt' + self.resp['Content-Language'] = 'en' # shouldn't be preserved + self.resp.set_cookie('key', 'value') + + new_response = ConditionalGetMiddleware().process_response(self.req, self.resp) + self.assertEqual(new_response.status_code, 304) + for header in ('Cache-Control', 'Content-Location', 'Date', 'ETag', 'Expires', 'Last-Modified', 'Vary'): + self.assertEqual(new_response[header], self.resp[header]) + self.assertEqual(new_response.cookies, self.resp.cookies) + self.assertNotIn('Content-Language', new_response) + class XFrameOptionsMiddlewareTest(SimpleTestCase): """