What limitations apply to opaque responses?
Asked Answered
S

1

112

Opaque responses are defined as part of the Fetch API, and represent the result of a request made to a remote origin when CORS is not enabled.

What practical limitations and "gotchas" exist around how opaque responses can be used, both from JavaScript and as resources on a page?

Snipe answered 23/8, 2016 at 20:3 Comment(0)
S
162

Access to Opaque Responses' Headers / Body

The most straightforward limitation around opaque responses is that you cannot get meaningful information back from most of the properties of the Response class, like headers, or call the various methods that make up the Body interface, like json() or text(). This is in keeping with the black-box nature of an opaque response.

Using Opaque Responses as Resources on a Page

Opaque responses can be used as a resource on a web page whenever the browser allows for a non-CORS cross-origin resource to be used. Here's a subset of elements for which non-CORS cross-origin resources, and therefor opaque responses, are valid, adapted from the Mozilla Developer Network documentation:

  • <script>
  • <link rel="stylesheet">
  • <img>, <video>, and <audio>
  • <object> and <embed>
  • <iframe>

A notable use case for which opaque responses are not valid is for font resources.

In general, to determine whether you can use an opaque response as a particular type of resource on a page, check the relevant specification. For example, the HTML specification explains that non-CORS cross-origin (i.e. opaque) responses can be used for <script> elements, though with some limitations to prevent leaking error information.

Opaque Responses & the Cache Storage API

One "gotcha" that developer might run into with opaque responses involves using them with the Cache Storage API. Two pieces of background information are relevant:

  • The status property of an opaque response is always set to 0, regardless of whether the original request succeeded or failed.
  • The Cache Storage API's add()/addAll() methods will both reject if the responses resulting from any of the requests have a status code that isn't in the 2XX range.

From those two points, it follows that if the request performed as part of the add()/addAll() call results in an opaque response, it will fail to be added to the cache.

You can work around this by explicitly performing a fetch() and then calling the put() method with the opaque response. By doing so, you're effectively opting-in to the risk that the response you're caching might have been an error returned by your server.

const request = new Request('https://third-party-no-cors.com/', {
  mode: 'no-cors',
});

// Assume `cache` is an open instance of the Cache class.
fetch(request).then(response => cache.put(request, response));

Opaque Responses & the navigator.storage API

In order to avoid leakage of cross-domain information, there's significant padding added to the size of an opaque response used for calculating storage quota limits (i.e. whether a QuotaExceeded exception is thrown) and reported by the navigator.storage API.

The details of this padding vary from browser to browser, but for Google Chrome, this means that the minimum size that any single cached opaque response contributes to the overall storage usage is approximately 7 megabytes. You should keep this in mind when determining how many opaque responses you want to cache, since you could easily exceeded storage quota limitations much sooner than you'd otherwise expect based on the actual size of the opaque resources.

Snipe answered 23/8, 2016 at 20:3 Comment(12)
Great answer! So how can we deal with the fonts? DevTools screenshotCulicid
Got the answer at googlechrome.github.io, I forgot to clone the request.Culicid
Does the 7 MB storage padding constitute an actual storage usage, or is it just the fake value returned by the API?Evite
It does not actually take up that amount of space on a device's physical storage. It's just the value that contributes to the quota calculations.Snipe
Your answer is even mentioned in the Workbox guide here: developers.google.com/web/tools/workbox/guides/…Carduaceous
True, but I did write that Workbox guide :-)Snipe
Does that make using an image CDN in conjunction with this kind of cache a bad design? (Wasting allocated space) Is it possible to cache a file retrieved from our main domain and expose it with the CDN link (key)? For example can I have a network request go to cdn.x.com/test.jpg and cache requests go to the main domain www.x.com/test.jpg.Lecce
I found a trickery around this problem, I have no idea if that's a decent solution but I basically make my service worker pretend it is the CDN. I add domain relative URLs in cache (eg., /test.jpg then for each fetch request to cdn.x.com/test.jpg I modify the URL with the origin domain (the URL becomes www.x.com/test.jpg), I use something like this: const cacheUrl = (url.hostname == 'cdn.x.com')? new URL(event.target.location.origin + url.pathname): url;. I then request the cache with this new url caches.match(cacheUrl), that seems to work fine. Any though on this approach?Lecce
@cglacet. I tried that, but the files still appear as "opaque" and a large size is still reported to the storage API.Gitagitel
“Opaque responses can be used as a resource on a web page whenever the browser allows for a non-CORS cross-origin resource to be used.” – How do I use the response in any of these elements? For example, HTMLObjectElement.data requires an URL, but can I convert an opaque response into an URL?Ingrid
If an opaque response is like a black-box, then what if it's a broken response? e.g., if we've cached it, then later when it's used, it will be a broken response used...?Mitsukomitt
Took me a while to understand but I think that with <script> you didn't mean the code inside, but the src attribute, hence this is why you can maybe get js scripts from almost anywhere ?Phrasing

© 2022 - 2024 — McMap. All rights reserved.