17

Our website is having problems loading CSS and JS resources on a Amazon S3 bucket with the very latest version of Chromium (Version 33.0.1722.0 - 237596) and Chrome Canary. It works well with any of the other browsers including the current Chrome (31.0.1650.57).

The error is:

Script from origin 'https://mybucket.s3.amazonaws.com' has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://app.example.com' is therefore not allowed access.

Our S3 CORS configuration on the resource bucket is:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>300000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Is it a bug with Chromium? Did something change on the latest CORS spec?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Chris Cinelli
  • 4,679
  • 4
  • 28
  • 40
  • Is it possible there's another header being sent by the particular browser that also needs to be included in an ? Or possibly *? – Michael - sqlbot Nov 28 '13 at 21:29
  • I was reading around and I read about *. I read in another QA here on Stackoverflow that according to the spec you cannot use "*" in (I did not check the specs). Just in case I tried to add it anyway and I did not see any change (i.e. the error persist). – Chris Cinelli Dec 02 '13 at 23:09
  • 1
    I have the same issue, can I know what did you do to solve this issue? – Vysakh Sreenivasan Feb 24 '14 at 12:07

4 Answers4

21

Add any query parameter such as ?cacheblock=true to the url, like so:

Instead of: https://somebucket.s3.amazonaws.com/someresource.pdf

do: https://somebucket.s3.amazonaws.com/someresource.pdf?cacheblock=true

The technical explanation I don't have entirely down. But it is something like the following:

Including a query parameter will prevent the 'misbehaving' caching behavior in Chrome, causing Chrome to send out a fresh request for both the preflight request and the actual request, allowing the proper headers to be present on both requests, allowing S3 to respond properly. Approximately.

nassan
  • 716
  • 7
  • 18
  • This answer help me a lot :) Thank you so much :) I upvote your answer :D – Jrey Aug 07 '18 at 08:00
  • Right @AnujTBE, the main idea here is that there should be some sort of query paramater – nassan Jun 21 '20 at 08:06
  • Thank you. I searched for hours to solve an issue setting a canvas background with XMLHttpRequest! – brad Dec 17 '20 at 15:21
  • Thank you very much! I spent almost 2 hours by testing my app only to realize what is causing the problem. Now you also gave me the answer what is the source of this behavior. In my case this issue happened to me when I was loading one image twice. I needed the same image to be loaded into the 3D model as a texture and secondly as a 2D texture thumbnail. – David Holada Jan 14 '21 at 22:33
14

Amazon released a fix for this a few months back. We were seeing the errors in current versions of Chrome & Safari (did not check Firefox). For anyone still running into this problem, try the following configuration:

S3 bucket CORS policy:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
  </CORSRule>
</CORSConfiguration>

CloudFront distribution settings (Behavior tab):

  1. Allowed HTTP Methods: GET, HEAD, OPTIONS
  2. Forward headers: Whitelist
  3. Whitelist headers: Origin, Access-Control-Request-Headers, Access-Control-Request-Method

We are hosting css and static javascript files via CloudFront with an S3 origin. We reference our javascript files via <script crossorigin="anonymous" src="http://assets.domain.com/app.js">.

EDIT

We began seeing this issue again with Safari 10.1.2. It turns out that we were accessing the Javascript file in two ways...

On page A via <script crossorigin="anonymous" src="http://assets.domain.com/app.js">. On page B via $.ajax() (so that it was lazy loaded).

If you went to page A -> page B -> page A, we would get a cross origin denied error. We took out the lazy loading approach and it solved our issue (again).

dignoe
  • 1,013
  • 11
  • 17
  • this makes sense but do you have to wait for it to propagate? if so, how long? I know there is a setting for default TTL which is 24 hours (by default), is that the amount of time you need to wait for this change to take effect? – Chris Scott Aug 05 '15 at 03:07
  • You just have to wait for the Cloudfront distribution settings to propagate to all of the edge servers. Took us 15-20 mins to be back online. – dignoe Aug 05 '15 at 21:23
11

In all likelihood, you're running into a very well-known problem with S3/CloudFront/CORS. The best solution I've been able to find is to have an app that proxies between S3 and CloudFront, always adding the appropriate CORS headers to the objects as they come back.

S3 + CloudFront are broken when it comes to serving CORS assets to different web browsers. The issue is two-fold.

  • Not all browsers require CORS for web fonts and other static assets. If one of these browsers makes the request, S3 won't send the CORS headers, and CloudFront will cache the (unhelpful) response.
  • CloudFront doesn't support the Vary: Origin header, so it has issues with using * for the AllowedOrigin value, and will only cache the first of multiple AllowedOrigin values.

In the end, these two issues make S3 + CloudFront an untenable solution for using CORS with a (fast) CDN solution — at least, out of the box. The bulletproof solution is to create a simple app that proxies the requests between S3 and CloudFront, always adding the requisite CORS headers so that CloudFront always caches them.

Request against a “Cold” cache

  • ← Browser requests a static asset from CloudFront.
  • ← CloudFront misses, and hits its origin server (a Proxy App).
  • ← The Proxy App passes the request to S3.
  • → S3 responds back to the Proxy App.
  • → The Proxy App adds the correct CORS headers (whether S3 had sent them or not). The Proxy App responds back to CloudFront.
  • → CloudFront caches the result and responds back to the browser.

Request against a “Warm” cache

  • ← Browser requests a static asset from CloudFront.
  • → CloudFront hits, and responds back to the browser.

Yes, this is a well-known, widespread issue:

I can say that our S3 and CloudFront teams are well-aware of the issues discussed here. By writing up a simple app that can act as a proxy between S3 and CloudFront, you can manually inject all of the correct CORS response headers before CloudFront caches them.

If you always work in Firefox, then you likely won't notice the issue — CloudFront will always be caching your CORS-enabled responses. If you work primarily in Safari or Chrome, you'll see it much more often when you switch back to a browser which requires these headers (Firefox and IE). Also, if you have separate development/staging/production environments, you're likely to run into the multi-origin issues more often.

Community
  • 1
  • 1
Ryan Parman
  • 6,855
  • 1
  • 29
  • 43
  • Where is this "well-known problem with S3/CloudFront/CORS" documented? And why it works fine in the current Chrome and not in Chrome Canary? – Chris Cinelli Dec 02 '13 at 20:38
  • The spec says that web fonts are supposed to use CORS. So far, IE and Firefox are the only ones that implement the spec. Safari, Chrome and Opera do not. It's probable that Chrome (and in-turn Opera) are changing their implementation to follow the spec. – Ryan Parman Dec 02 '13 at 22:00
  • 1
    current version of Firefox does not have problems. – Chris Cinelli Dec 02 '13 at 23:05
  • It very much does. Need more space to explain, so see my follow-up answer. – Ryan Parman Dec 03 '13 at 00:50
  • This is good stuff. Regarding my question: (1) We do not use CloudFront. (2) Our page works on the current version of Chrome and Firefox. Not on the latest. (3) I field a bug on Chromium and they are currently looking at it. It may be a regression problem. – Chris Cinelli Dec 03 '13 at 19:29
  • If you write a proxy you give up on the CDN benefits of cloudfront. – Bjorn Dec 06 '13 at 21:37
  • @Bjorn Tipling: Please re-read the flow I discussed before commenting. The proxy is on the source side, not the cached side. Once CloudFront caches the content in its CDN, it's cached, causing no issues with how a CDN works. – Ryan Parman Dec 08 '13 at 07:36
  • @RyanParman You mentioned that some browsers don't require CORS. Do you know which ones? – chchrist May 12 '14 at 15:46
  • At present, Safari and Chrome do not enforce CORS on web fonts. Firefox and (modern) Internet Explorer do. – Ryan Parman May 18 '14 at 06:23
  • I've put in an answer on a similar question where I've Cloudfront and CORS working without a query string hack: http://stackoverflow.com/questions/12229844/amazon-s3-cors-cross-origin-resource-sharing-and-firefox-cross-domain-font-loa/25305915#25305915 – Eamonn Gahan Aug 14 '14 at 10:37
  • I know this discussion was a while ago, but the original question asked about scripts, not web fonts. I've just run into that. I'd always though script tags were exempt from this rule (and that's why JSONP was popular as a workaround to browser restrictions). Are script tags now subject to this, does this mean JSONP doesn't work now, and is there documentation on this somewhere I can't seem to find? – Marc Stober Dec 18 '15 at 13:21
7

Wanted to chime in with an alternate theory to this old question: Chrome has a bug/"feature" that's been present since at least Aug 2014 that causes a cross-origin request to fail if the resource was first loaded via a normal fetch, apparently because Chrome caches the CORS-less resource headers and then refuses to give the cached resource to the cross-origin request.

To make matters worse in our testing in a complex scenario, it isn't even necessarily fully consistent between refreshes (because order of resource loading?) and other browsers don't appear to share the behavior.

It was fun bug hunt! It seems that simply adding crossorigin='anonymous' to any tags loading the resource forces Chrome to pull the CORS headers in, fixing the subsequent cross-origin requests.

Pogodan
  • 71
  • 1
  • 1
  • This pointed me in the right direction. In my case I was using an OBJECT tag to load a PDF file for the first time, so the crossorigin attribute was not available. So before setting the PDF url to the OBJECT, I fetched the PDF with a regular ajax request. That solved my problem. – Daniel Facciabene Sep 09 '20 at 15:43