最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin1浏览0评论

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="/">
    <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' .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 = '.jpg';

imgCloudfront = new Image();
imgCloudfront.crossOrigin = '';
imgCloudfront.onload = function() {
 $('body').append('image (cloudfront) loaded<br>');
}
imgCloudfront.src = '.jpg';
<script src=".11.1/jquery.min.js"></script>

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>

Share Improve this question edited Apr 23, 2015 at 4:09 Robin Pyon asked Apr 2, 2015 at 17:02 Robin PyonRobin Pyon 7591 gold badge10 silver badges19 bronze badges 2
  • If image is rendering fine in all browsers except safari 8, the issue should not be at the image end. Can you please provide the image url to try out ?? – Language Lassi Commented Apr 20, 2015 at 6:35
  • 1 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. – don.najd Commented Oct 27, 2015 at 0:37
Add a comment  | 

5 Answers 5

Reset to default 7 +50

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.

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

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.

Try to clean browser cache.

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

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

发布评论

评论列表(0)

  1. 暂无评论