What limitations apply to opaque responses?

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.

Leave a Comment