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?
An opaque response is for a request made for a resource on a different origin that doesn't return CORS headers. With an opaque response we won't be able to read the data returned or view the status of the request, meaning we can't check if the request was successful or not.
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:
status
property of an opaque response is always set to 0
, regardless of whether the original request succeeded or failed.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With