What Limitations Apply to Opaque Responses

What is an opaque response, and what purpose does it serve?

Consider the case in which a service worker acts as an agnostic cache. Your only goal is serve the same resources that you would get from the network, but faster. Of course you can't ensure all the resources will be part of your origin (consider libraries served from CDNs, for instance). As the service worker has the potential of altering network responses, you need to guarantee you are not interested in the contents of the response, nor on its headers, nor even on the result. You're only interested on the response as a black box to possibly cache it and serve it faster.

This is what { mode: 'no-cors' } was made for.

Fetch API; why use the no-cors mode if the response is Opaque?

Remember that CORS settings doesn't prevent a request from reaching the server - that's what authentication and CSRF is for. Instead, it prevents responses from being read by the page. The request is still:

  1. sent from the page,
  2. the browser adds the Origin header,
  3. it's still processed by the server (generally - CORS has nothing to do with this, although there maybe other security in place),
  4. and when returned the browser checks the response header for Access-Control-Allow-Origin. If it matches what the browser thinks is the Origin then the browser lets the page see the result of the request.

That's the key - Same-origin policy and CORS settings disallow the page in a cooperating browser from seeing the response.

Note also there is a step 0. above, which is an OPTIONS "pre-flight check" is sent to check that if the request was to go through would the page be allowed to see the result? If not, then they assume there is little point in sending the request - but that is an assumption.

Now to address the question

mode: no-cors does two things:

  1. It says that I don't need to see the result
  2. and so it doesn't send the pre-flight check

Off the top of my head, here's what I think I could use it for (some of them nefarious)

  • Any time when I don't need to see the response, such logging, tracking, or hacking; when the frontend code is essentially a try { const notNeeded = fetch(...) } catch { console.log('Tough luck, do nothing') }
  • Any time I want to send the data to the server as fast as possible by not sending a preflight check. I can always send a GET with CORS later on to read the data when I really need it.
  • Caching, detailed in this answer

Reminder

CORS does the above. It doesn't do the following:

  • Prevent the server from processing the request - that's what Authentication and CSRF prevention is for.
  • Stop spoofing the Origin header - this works with only cooperating browsers. And as an attacker, you generally don't have access to what browser a user is using. Because the header can be spoofed, a server shouldn't use the data in it for security. (And this is why when testing browser APIs through a tool like CURL/Postman/Insomnia you need to check CORS headers are coming through, because they accept all responses and CORS policies are never applied.)

How to use an API with opaque response?

An opaque response is one you cannot see the content of. They aren't useful in of themselves.

Setting mode: 'no-cors' is a declaration that you don't need to read the response (or do anything else that requires CORS permission).

For example, the JavaScript might be sending analytics data to be recorded by a server.

The benefit of no-cors mode is that it lets you send the data without getting exceptions reported in the JS console (which would (a) look bad if anyone opened it and (b) flood the console with junk that makes it hard to find real errors).


If you need to access the response, don't set mode: 'no-cors'. If it is a cross origin request then you will need to use some other technique to bypass the Same Origin Policy.


Aside: "Access-Control-Allow-Origin": "*" is a response header. Do not put it on a request. It will do nothing useful and might turn a simple request into a preflighted request.

Service Worker - TypeError: Request failed

This is a side-effect of dealing with opaque responses (those fetched with mode: 'no-cors'). Here's an excerpt from this longer answer:

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

Images from S3 not working with Service Worker

My guess is that your image responses from S3 are opaque. (See: What limitations apply to opaque responses?)

If you enable CORS in S3, and add the crossorigin attribute to your <img> tags, then your responses should all be CORS-enabled.

Alternatively, you can work around the fact that you're getting opaque responses by not checking the value of response.ok in your addToCache() function—it'll always be false for opaque responses. This means that you might end up adding a 4xx or 5xx error response to the cache instead of a valid image response, but that's the risk you run when caching opaque responses.

Accessing in Chrome POST response body from Cloudflare Worker

This behaviour is due to the no-cors config of the fetch-api.
For more details read about opaque requests here:

What limitations apply to opaque responses?



Related Topics



Leave a reply



Submit