Working with the Fetch API to fetch resources, often, we can run into CORS warnings. Sometimes the warning contains a reference to an "opaque response". In this article, we'll take a look at this mysterious response and learn what it means.
Let's go ahead and walk through a really simple example. Let's say that we want to retrieve an image from the web using the Fetch API. The code for this looks simple:
fetch(
'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png'
)
.then((response) => {
return console.log(response);
})
.catch((error) => {
return console.log(error);
});
Checking the DevTools console will, of course, display the following message:
Failed to load https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '[...]' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
To learn more about CORS itself, please read "What is CORS?"
Now let's drill down a little bit and try to understand the second part of the error. It states that if we want to have this mysterious opaque response, we should set the no-cors
mode. Let's do that and examine what happens:
fetch(
'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png',
{
mode: 'no-cors',
}
)
.then((response) => {
return console.log(response);
})
.catch((error) => {
return console.log(error);
});
Okay, this time around we do get a response:
Response {
body: null
bodyUsed: false
headers: Headers
ok: false
redirected: false
status: 0
statusText: ""
type: "opaque"
url: ""
}
What we see above is the opaque response. There are a couple of things to note:
status
is "0" (and not a regular HTTP status code - like 200)statusText
is emptyheaders
object is also emptyBasically, we can't see anything in this Response
object - and henceforth the name opaque
.
However, what is the purpose of such a response? Does it serve anything?
The answer is "yes, of course". Think about the following: we are developing a PWA (Progressive Web App) where we'd be using the Service Worker to cache assets. We can still cache an opaque response and later on serve it from the cache only. In other words, we retrieve a resource, but we don't care about the headers, the actual response or result.
The browser does allow us to access cross-origin resources for a subset of elements including <script>
, <img>
, <video>
, <audio>
, <link rel="stylesheet">
, <object>
, <embed>
and <iframe>
.
It's important to remember that the Cache Storage API's add()
and addAll()
methods only allow to cache results where the status code is in the 2xx range - so caching an opaque response fails.
Workbox.js is a collection of JavaScript libraries for creating PWAs. Think about it as a wrapper around the Cache API, Service Workers and other APIs that would help you to create a PWA in an easy manner.
For some situations, it makes sense to allow the caching of an opaque response, but for some, it doesn't.
Think about the following - since an opaque response doesn't return a valid status code we could - using a cache first strategy - end up caching a broken response and serve that later on subsequent loads. This is not what we'd like to achieve.
The cache first strategy means that we first try to serve a response from the cache. If we don't have an available cached asset, we will fall back to the network, return the response as well as cache it.
However, there are certain strategies such as the network first, or the stale while revalidate where it makes sense to cache, and opaque response since these caches are short-lived and therefore having an inadequate response would be quickly updated/removed.
Opaque responses are due to security mechanisms in the browsers for cross-origin requests. Certain elements on a webpage can access resources cross-origin as well. Some requests can be read via JavaScript if the server returns CORS headers, but this is down to the origin's implementation. Receiving a response via the Fetch API is possible by passing in a { mode: 'no-cors' }
option but in that case, we receive the response as a "black box" - no information is available us to view, but we can still cache this response.