diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index 040d13aad20ad113e344a83017564b6d724bfb16..6d382c92333f2873543988699d36a002dfc7caa3 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -1046,7 +1046,7 @@ class Binary(http.Controller):
             filename_field=filename_field, download=download, mimetype=mimetype,
             default_mimetype='image/png', access_token=access_token)
 
-        if status == 301 or (status != 200 and download):
+        if status in [301, 304] or (status != 200 and download):
             return request.env['ir.http']._response_by_status(status, headers, content)
         if not content:
             content = base64.b64encode(self.placeholder(image='placeholder.png'))
diff --git a/addons/web/tests/test_image.py b/addons/web/tests/test_image.py
index e17073d0fb723028aceacc48ce8d8adf21c4c477..d26528b88e36215097f415c66ec2e748e3c31fc7 100644
--- a/addons/web/tests/test_image.py
+++ b/addons/web/tests/test_image.py
@@ -2,6 +2,7 @@
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
 
 import io
+import base64
 
 from PIL import Image
 
@@ -42,3 +43,23 @@ class TestImage(HttpCase):
         response = self.url_open('/web/image/fake/0/image_no_size')
         image = Image.open(io.BytesIO(response.content))
         self.assertEqual(image.size, (256, 256))
+
+    def test_02_content_image_Etag_304(self):
+        """This test makes sure that the 304 response is properly returned if the ETag is properly set"""
+
+        attachment = self.env['ir.attachment'].create({
+            'datas': b"R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=",
+            'name': 'testEtag.gif',
+            'public': True,
+            'datas_fname': 'testEtag.gif',
+            'mimetype': 'image/gif',
+        })
+        response = self.url_open('/web/image/%s' % attachment.id)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(base64.b64encode(response.content), attachment.datas)
+
+        etag = response.headers.get('ETag')
+
+        response2 = self.url_open('/web/image/%s' % attachment.id, headers={"If-None-Match": etag})
+        self.assertEqual(response2.status_code, 304)
+        self.assertEqual(len(response2.content), 0)
diff --git a/odoo/addons/base/models/ir_http.py b/odoo/addons/base/models/ir_http.py
index 293269d61d1f848ca554a275a1b34e316cbc2dad..8920c8e9a6b2b873c2e1a7296236d49ca0973c35 100644
--- a/odoo/addons/base/models/ir_http.py
+++ b/odoo/addons/base/models/ir_http.py
@@ -345,9 +345,11 @@ class IrHttp(models.AbstractModel):
         headers = [('Content-Type', mimetype), ('X-Content-Type-Options', 'nosniff')]
         # cache
         etag = bool(request) and request.httprequest.headers.get('If-None-Match')
-        status = status or (304 if filehash and etag == filehash else 200)
+        status = status or 200
         if filehash:
             headers.append(('ETag', filehash))
+            if etag == filehash and status == 200:
+                status = 304
         headers.append(('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0)))
         # content-disposition default name
         if download:
@@ -390,11 +392,11 @@ class IrHttp(models.AbstractModel):
             status, content, filename, mimetype, filehash = self._binary_ir_attachment_redirect_content(record, default_mimetype=default_mimetype)
         if not content:
             status, content, filename, mimetype, filehash = self._binary_record_content(
-                record, field=field, filename=None, filename_field=filename_field,
+                record, field=field, filename=filename, filename_field=filename_field,
                 default_mimetype='application/octet-stream')
 
         status, headers, content = self._binary_set_headers(
-            status, content, filename, mimetype, unique, filehash=False, download=download)
+            status, content, filename, mimetype, unique, filehash=filehash, download=download)
 
         return status, headers, content
 
diff --git a/odoo/tests/common.py b/odoo/tests/common.py
index 1c4f87d75acadf4154bebdc35b12e105a6960773..92e6fe0addf73bd54ad537a97c7e2149220e0bc1 100644
--- a/odoo/tests/common.py
+++ b/odoo/tests/common.py
@@ -822,12 +822,12 @@ class HttpCase(TransactionCase):
         self.opener = requests.Session()
         self.opener.cookies['session_id'] = self.session_id
 
-    def url_open(self, url, data=None, timeout=10):
+    def url_open(self, url, data=None, timeout=10, headers=None):
         if url.startswith('/'):
             url = "http://%s:%s%s" % (HOST, PORT, url)
         if data:
-            return self.opener.post(url, data=data, timeout=timeout)
-        return self.opener.get(url, timeout=timeout)
+            return self.opener.post(url, data=data, timeout=timeout, headers=headers)
+        return self.opener.get(url, timeout=timeout, headers=headers)
 
     def _wait_remaining_requests(self):
         t0 = int(time.time())