diff --git a/django/utils/cache.py b/django/utils/cache.py index 2b37acfd44..3ee01a1e94 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -98,7 +98,7 @@ def get_max_age(response): def set_response_etag(response): - if not response.streaming: + if not response.streaming and response.content: response['ETag'] = quote_etag(hashlib.md5(response.content).hexdigest()) return response diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index 7d1948b183..7fec7246d5 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -248,6 +248,10 @@ Miscellaneous ``/`` if not set). This change should not affect settings set to valid URLs or absolute paths. +* :class:`~django.middleware.http.ConditionalGetMiddleware` no longer adds the + ``ETag`` header to responses with an empty + :attr:`~django.http.HttpResponse.content`. + .. _deprecated-features-3.1: Features deprecated in 3.1 diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py index def313c5f7..6b6eded24d 100644 --- a/tests/middleware/tests.py +++ b/tests/middleware/tests.py @@ -452,6 +452,12 @@ class ConditionalGetMiddlewareTest(SimpleTestCase): res = StreamingHttpResponse(['content']) self.assertFalse(ConditionalGetMiddleware().process_response(self.req, res).has_header('ETag')) + def test_no_etag_response_empty_content(self): + res = HttpResponse() + self.assertFalse( + ConditionalGetMiddleware().process_response(self.req, res).has_header('ETag') + ) + def test_no_etag_no_store_cache(self): self.resp['Cache-Control'] = 'No-Cache, No-Store, Max-age=0' self.assertFalse(ConditionalGetMiddleware().process_response(self.req, self.resp).has_header('ETag'))