Images with Cache-Control:no-cache + ETag are not validated each time they are used.
Reported by
jens.neh...@gmail.com,
Jul 6 2017
|
||||
Issue descriptionUserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4 Example URL: https://jsfiddle.net/pvmch40v/1/ Steps to reproduce the problem: 1. Open new Tab and Dev Tools Network inspector. 2. Open https://jsfiddle.net/pvmch40v/1/ What is the expected behavior? Given the jsfiddle example, the image "no-cache-test.gif" has HTTP header "Cache-Control: private, max-age=0, no-cache" and an associated ETag. Chrome should use a conditional request to validate if the image is still up to date whenever a new img element with that same image URL is inserted to DOM. In our specific case we have a single page web application that uses a single URL for, e.g. a profile picture (example.com/user/[user-id]/profile-picture?w=100&h=100). If a user uploads a new profile picture to replace the previous one we expect that the browser loads the updated image on the next usage of that same URL because we use the no-cache + Etag headers. What went wrong? jsfiddle example: Chrome just makes a single request for the image and no further conditional requests (If-None-Match) for validation even tough new images are added over time. Our web application: The behavior is pretty unpredictable. Occasionally Chrome sends conditional request for validation but often it does not. For example we have a UI that shows a list of users on the left and user details on the right, including profile picture. When switching between users, their profile picture is occasionally validated but most of the time no conditional request is made at all and the cached version is used. DOM of the details area will be rebuild each time a user selection changes, thus it is a new img element each time, similar to the jsfiddle example. In case Chrome does not make a conditional request net-internals -> events does not show any attempt to make a request. IMHO that is clearly against the RFC, however Firefox and IE 11 / Edge behave similar to Chrome. Only Safari behaves correctly. Currently our only workaround is to change the URLs for the profile picture of a user if the picture has been updated, e.g. by adding a content hash like example.com/user/[user-id]/profile-picture-[MD5 of picture]?w=100&h=100. But that has the downside that the URL needs to be generated by the server or the hash needs to be send to the web application before the web application can actually use the URL. Why does Chrome behave that way? Isn't it a valid use case? Isn't the RFC clear how the browser must behave? Did this work before? N/A Chrome version: 61.0.3141.7 Channel: dev OS Version: OS X 10.12.5 Flash Version:
,
Jul 6 2017
I believe this is due to Blink's MemoryCache and not the disk cache. Changing components.
,
Jul 6 2017
This behavior is implemented around MemoryCache, and its basis is in the HTML spec: https://html.spec.whatwg.org/multipage/images.html#the-list-of-available-images
,
Jul 6 2017
Mmmh so the spec says whenever an image has been fetched successfully add it to the list of available images with the "ignore higher level cache" flag set to avoid re-downloading the image again even if caching is not allowed for that image. The only way to unset the flag is by copying entries from one list of available images to another of a different document. Alternatively an image might get removed from that list completely because of memory pressure. So if we ignore memory pressure for now, then an image is only downloaded once per document context and the sentence "User agents must remove entries in the list of available images as appropriate given higher-layer caching semantics for the resource (e.g. the HTTP `Cache-Control` response header) when the ignore higher-layer caching flag is unset" only applies for the case the image entry has been copied from one document list to another. Following the above argumentation would mean that images marked with Cache-Control:no-store should also only be loaded once in the context of a document. However Chrome re-downloads such an image every time (you can update the jsfiddle using no-store-test.gif). You might now say that the Fetch Spec says that resources marked as no-store are not updated in the HTTP Cache (https://fetch.spec.whatwg.org/#concept-fetch), but I would argue that the "list of available images" isn't the HTTP Cache. So Chrome seems to make an exception for the no-store option already, intentionally or unintentionally. I feel like the image spec is too strict because if an image is marked as no-cache or must-revalidate then the server is fine with caching as long as the cached content isn't stale. Usually the user agent does not re-download such an image which is also the goal of the "list of available images" cache. So maybe no-cache should be respected instead of being ignored. So overall I have the impression that using a fixed URL to represent a dynamic image whose content rarely change is a no-go nowadays? Kind of disappointing.
,
Jul 7 2017
Ok as a workaround I can use XMLHttpRequest 2.0 + Blob + URL.createObjectURL(). https://jsfiddle.net/74r8jftv/ The request through XMLHttpRequest respects no-cache semantics.
,
Jul 13 2017
#4 - that's right, spec says UA should avoid re-downloading the same image if they're in the same document (so either no-cache or no-store doesn't work), while Chrome/Blink currently breaks the spec when no-spec is specified. Whether Chrome/Blink should change the behavior for no-spec case too is still kinda debatable. Anyways marking this WontFix |
||||
►
Sign in to add a comment |
||||
Comment 1 by b...@chromium.org
, Jul 6 2017