With <script crossorigin='anonymous'>, why is a script "blocked by CORS policy"?

JavascriptHtml

Javascript Problem Overview


With Google Chrome or Firefox, if I try to load the following HTML:

<script crossorigin='anonymous' src='https://stackoverflow.com/foo.js'></script>

I get a CORS error like this:

> Access to Script at 'https://stackoverflow.com/foo.js'; from origin 'https://stackoverflow.com'; has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource...

However, the same tag without the crossorigin='anonymous' attribute works fine (of course generating a 404 error, since foo.js does not exist).

This is surprising, since anonymous is just supposed to prevent sending any credentials, and script tags are not supposed to require CORS. What is causing this, and what should I do?

Javascript Solutions


Solution 1 - Javascript

I was confused about this for a while. Here's how I now understand it:

According to the W3C, there are actually three possible values for the crossorigin attribute: anonymous, use-credentials, and an "missing value default" that can only be accessed by omitting the attribute. (An empty string, on the other hand, maps to anonymous.) The default value causes the browser to skip CORS entirely, which is the normal behavior I was expecting.

The crossorigin attribute should only be used if we care about getting error information for the script being loaded. Since accessing this information requires a CORS check, the Access-Control-Allow-Origin header must be present on the resource for it to be loaded.

Solution 2 - Javascript

crossorigin attribute has only two possible values: anonymous or use-credentials. Any value other than anonymous, including empty value, will be translated to anonymous.

So these three tags have the same meaning:

<script src="https://stackoverflow.com/foo.js" crossorigin="anonymous">
<script src="https://stackoverflow.com/foo.js" crossorigin="">
<script src="https://stackoverflow.com/foo.js" crossorigin="IamCrazy">

What is interesting though, is that CORS behavior is totally disabled if you skip crossorigin attribute. For example:

<script src="https://stackoverflow.com/foo.js">

This tag will run script without any CORS-related checking. In practice, no crossorigin attribute makes browser skip Origin HTTP header entirely.

No matter if your crossorigin is anonymous or use-credentials, request's Origin must still match response's Access-Control-Allow-Origin. Otherwise no luck - script is never fired.

Source: HTTP access control (CORS) on Mozilla Developer Network

Solution 3 - Javascript

It's a bit old thread, but it's something that I happened to encounter, these days. In addition to the crossorigin, you should also make sure that the server from which you are loading the script (in your example - stackoverflow.com) returns a specific header.

'Access-Control-Allow-Origin' '*';

Only then you will be able to receive the full information about an errors which has happened in the script.

Of course if you know for sure which url to allow you should use it instead of the asterisk '*'.

Solution 4 - Javascript

> This is surprising, since anonymous is just supposed to prevent sending any credentials, and script tags are not supposed to require CORS. What is causing this

The use of the word "just" there is incorrect.

See MDN on onerror:

> When an error occurs in a script, loaded from a different origin, the details of the error are not reported to prevent leaking information (see bug 363897). Instead the error reported is "Script error." This behavior can be overridden in some browsers using the crossorigin attribute on <script> and having the server send the appropriate CORS HTTP response headers.

Adding any crossorigin attribute value is supposed to allow you to do things which require CORS permission. In the case of a script that is to get useful error messages reported to onerror as shown in the quote above.

The cost of that is that you now require CORS permissions to do anything with the data. In the case of a script element, that includes executing the script (which, as you pointed out, normally doesn't require CORS permission).

The difference between anonymous and use-credentials is that the latter will also:

  • Require a pre-flight OPTIONS request to get permission to send credentials (such as any cookies) before it will make the GET request for the script
  • Send any credentials in that GET request for the script

> what should I do?

You can either:

  • Not use the crossorigin attribute (which will send any credentials (such as cookies) to the server unless they get blocked by a third-party cookies policy in the browser)
  • Have the server grant permission using the Access-Control-Allow-Origin response header
  • Move the script to a different domain which doesn't have credentials associated with it (e.g. you could have www.example.com with cookies and cdn.example.com without).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionpdg137View Question on Stackoverflow
Solution 1 - Javascriptpdg137View Answer on Stackoverflow
Solution 2 - JavascriptRobo RobokView Answer on Stackoverflow
Solution 3 - JavascriptBoncho ValkovView Answer on Stackoverflow
Solution 4 - JavascriptQuentinView Answer on Stackoverflow