What is an opaque response?

This post is 4 years old. (Or older!) Code samples may not work, screenshots may be missing and links could be broken. Although some of the content may be relevant please take it with a pinch of salt.

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?"

Opaque response?

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:

  • the status is "0" (and not a regular HTTP status code - like 200)
  • the statusText is empty
  • the headers object is also empty

Basically, we can't see anything in this Response object - and henceforth the name opaque.

Use case for an opaque response

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

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.

Conclusion

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.