Skip to main content

What is an opaque response?

3 min read

Older Article

This article was published 8 years ago. Some information may be outdated or no longer applicable.

When you use the Fetch API to grab resources, you’ll sometimes run into CORS warnings. And sometimes those warnings mention an “opaque response”. Let’s figure out what that actually means.

Here’s a simple example. Say we want to fetch an image from the web:

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);
  });

Open DevTools and you’ll see this in the console:

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, read “What is CORS?”

Opaque response?

Let’s look at the second half of that error. It says we can get an opaque response by setting no-cors mode. Let’s try it:

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);
  });

This time we do get a response:

Response {
  body: null
  bodyUsed: false
  headers: Headers
  ok: false
  redirected: false
  status: 0
  statusText: ""
  type: "opaque"
  url: ""
}

That’s the opaque response. A few things stand out:

  • status is “0” (not a normal HTTP status code like 200)
  • statusText is empty
  • headers is empty

We can’t see anything inside this Response object. That’s why it’s called opaque.

Use case for an opaque response

So what’s the point? Can you actually do anything with it?

Yes. Think about PWAs (Progressive Web Apps) where a Service Worker caches assets. You can cache an opaque response and serve it from the cache later. You grab the resource, but you don’t need to inspect the headers or the response body.

Browsers already let us pull cross-origin resources for certain elements: <script>, <img>, <video>, <audio>, <link rel="stylesheet">, <object>, <embed> and <iframe>.

One catch: the Cache Storage API’s add() and addAll() methods only cache results with status codes in the 2xx range. Trying to cache an opaque response through those methods will fail.

Workbox.js

Workbox.js is a collection of JavaScript libraries for building PWAs. It wraps the Cache API, Service Workers, and related APIs into something more manageable.

Whether caching an opaque response makes sense depends on your strategy.

Consider this: since an opaque response doesn’t carry a valid status code, a cache-first strategy could store a broken response and keep serving it on every subsequent load. That’s not what you want.

Cache-first means we try to serve from the cache first. If there’s no cached asset, we fall back to the network, return the response, and cache it.

But strategies like network-first or stale-while-revalidate work well with opaque responses. Those caches get refreshed quickly, so a bad response won’t stick around for long.

Conclusion

Opaque responses exist because of browser security around cross-origin requests. Some HTML elements can fetch cross-origin resources natively. JavaScript can read responses if the server sends back CORS headers, but that’s up to the server. Passing { mode: 'no-cors' } to the Fetch API gives you a response, but it’s a black box. You can’t inspect it, but you can still cache it.