Unable to load cross-origin image (from CloudFront) in Safari

12,496

Solution 1

Double checkout your cloudfront behavior settings. Did you add the custom header Origin. It should be Access-Control-Allow-Origin. I recently setup cloudfront as well and found this article helpful: http://kennethjiang.blogspot.com/2014/07/set-up-cors-in-cloudfront-for-custom.html

I've had problems with clearing the cache on cloudfront. If you had requested an image before your CORS setup, cloudfront may just return what it had before so your new configurations are not reflected. You can try to run an invalidation, it's one of the cloudfront behavior tabs. I got inconsistent results with this. If you want to make sure it's working, upload a new image and test with that. I'm using rails and on occasion I bump up an asset versions which causes all my asset sto have a new fingerprint and that solves the caching issues because each file has a new name.)

Regarding your question about the query string. You probably got a cache miss from cloudfront. You can try to repeat the exercise with curl to see the response X-Cache: Hit from cloudfront is present or not. In the cloudfront behavior settings there is a config for 'Forward Query Strings'. That may play a factor.

In my setup, I had an intermediate varnish cache so I had to mess with that too to make sure all the headers made it through. Doesn't seem like you have that but it's something to watch out for.

Solution 2

See: https://stackoverflow.com/a/13147554/1994767

The Access-Control-Allow-Headers (<AllowedHeader>*</AllowedHeader>) header does not allow wildcards. It must be an exact match: http://www.w3.org/TR/cors/#access-control-allow-headers-response-header

Solution 3

That's just a guess, but the missing Access-Control-Allow-Origin header seems the key point to me. For some reasons other browsers can do without it but not Safari.
Have you tried tinkering with your CloudFront configuration? Like forwarding all headers instead of whitelisting, or changing query string forwarding setting. And as others have said, please provide a test url so we can check for ourselves.

Solution 4

Try to clean browser cache.

If this won't work then please provide the URL to an example image.

Solution 5

I am guessing that this is related to the browser cache.

There is an issue where S3 omits a "Vary: Origin" Header for Non-CORS-Requests. This leads to some browsers caching a Non-CORS response and reusing this cached response in subsequent requests that need CORS, causing the browser to throw the "Cross-origin image load denied by Cross-Origin Resource Sharing policy." error.

For more details see: S3 CORS, always send Vary: Origin

The problem has also been reported on the AWS S3 Forums

Share:
12,496

Related videos on Youtube

Robin Pyon
Author by

Robin Pyon

Updated on September 18, 2022

Comments

  • Robin Pyon
    Robin Pyon over 1 year

    I get the following error when trying to load an image from a CloudFront URL in Safari 8:
    Cross-origin image load denied by Cross-Origin Resource Sharing policy.

    This only happens on Safari 8. In FireFox 38 and Chrome 41 latest it loads just fine. (Mac 10.10)

    My setup:

    1. S3 bucket with the following CORS configuration

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

    2. A linked CloudFront distribution

    The following headers have been whitelisted (under behaviours):

    • Access-Control-Request-Headers
    • Access-Control-Request-Method
    • Origin

    3. JavaScript

    var img = new Image();
    img.crossOrigin = '';
    img.onload = function() {
      console.log('image loaded');
    }
    

    What I've tried:

    1. Checking returned headers from curl

    The image is returning the correct headers (notably Access-Control-Allow-Origin)

    > curl -sI -H 'Origin: localhost' -H 'Access-Control-Request-Method: GET' http://foo.cloudfront.com/image.jpg
    ...
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Methods: GET
    Access-Control-Max-Age: 3000
    Server: AmazonS3
    Vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
    X-Cache: Hit from cloudfront
    

    2. Checking returned headers in the browser

    Interestingly enough the image is NOT returning the Access-Control-Allow-Origin: * header in all three browsers. Why would this be the case?

    3. Adding a query string to the URL

    Adding a query string (e.g. ?foo) to the URL being loaded WILL cause the Access-Control-Allow-Origin header to be returned in the browser, and allows the image to be loaded in Safari! This is great, but why would adding the query string allow this to work (and also return the Access-Control-Allow-Origin header)?

    4. Loading an image from an S3 bucket (not tied to a CloudFront distribution)

    Loading an image from another bucket not tied to CloudFront (with an identical CORS config) also works just fine in Safari.

    Which initially led me to believe this was specifically a CloudFront issue, but the above point with the query string makes me think otherwise.

    This is driving me completely batty. Can anyone help shed some light on the above?


    Update

    Thanks for the replies. Frustratingly enough, I can't seem to replicate this issue.

    Below is a snippet which loads two images (one from an S3 bucket, another from its respective Cloudfront distribution) and they both also appear to load in just fine with the headers you'd expect, contrary to what I said above in point #2.

    Unfortunately I'm not really closer to a definite answer, but for now I'm just going to chalk it down to an error on my behalf, potentially requesting an image before my CORS setup as Derek suggested.

    var img, imgCloudfront;
    
    img = new Image();
    img.crossOrigin = '';
    img.onload = function() {
      $('body').append('image loaded<br>');
    }
    img.src = 'http://sandbox-robinpyon.s3.amazonaws.com/test.jpg';
    
    imgCloudfront = new Image();
    imgCloudfront.crossOrigin = '';
    imgCloudfront.onload = function() {
     $('body').append('image (cloudfront) loaded<br>');
    }
    imgCloudfront.src = 'http://d32d4njimxij7s.cloudfront.net/test.jpg';
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

    • don.najd
      don.najd over 8 years
      After hours of pain, I saw your post and added "?foo=bar" to my image src and it fixed this issue in safari 9. Technology disappointed me down today.
  • Robin Pyon
    Robin Pyon almost 9 years
    I did add the custom header Origin. Interestingly enough there are only three headers for me available to whitelist, Access-Control-Request-Headers, Access-Control-Request-Method and Origin. There isn't an Access-Control-Allow-Origin to whitelist like that article suggests. As I'm unable to recreate this issue I'm inclined to believe that it was a misconfiguration on Cloudfront on my behalf, though it doesn't really explain the Safari issue.
  • Robin Pyon
    Robin Pyon almost 9 years
    I've updated my question with a snippet which loads those URLs. This test example works fine and that Access-Control-Allow-Origin header is being returned across all browsers as you'd expect. As mentioned above, what's likely is that I updated my Cloudfront configuration after requesting an image... arg..
  • Derek
    Derek almost 9 years
    Right, the headers you can choose from do not include the Access-Control-* ones but you can type them in as custom headers. Seems like you got past the issue anyway :).
  • Robin Pyon
    Robin Pyon almost 9 years
    Very odd, for me it's just acts as a filter. Entering in new custom headers doesn't work. Hopefully I'm not missing something completely obvious? See: i.imgur.com/wsRioer.png
  • Derek
    Derek almost 9 years
    Yes, that is odd. I looked at some of my configs too. When the origin type is S3 Origin it's missing a Add Custum >> button like in cl.ly/image/0o1d3j3K2X1Z which is setup as a custom origin to pull from another website.