I’m using Next.js (front-end) at https://toscanini.me and serving images from https://images.toscanini.me via Amazon S3 + CloudFront. Despite having set up CORS, my browser still reports a CORS error when I try to load images. The preflight (OPTIONS) request looks successful in the logs from cURL, but the actual GET request triggers a CORS error in the browser console.
I'm using Photo-Sphere-Viewer to render 360 AVIF images. Just like this:
panoramaViewer = new Viewer({
container: viewerElement,
panorama: imageURL,
requestHeaders: {
"Sec-Fetch-Mode": "no-cors",
Origin: "https://toscanini.me",
},
withCredentials: true,
});
You can see all the other options here in the documentation, but the CORS related ones are withCredentials
and requestHeaders
. I played adding and removing the credentials, adding and removing headers. Many different combinations.
Anyways, at the end what this does it's just invoking a JS fetch
(which also doesn't work). So in summary, the problem it's just accessing CORS through fetch, although I mention the PSV library in case someone seems something related.
This is my CORS configuration on S3 Bucket
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET",
"HEAD"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": []
}
]
I can't add the OPTIONS method, as specified by S3 docs. But I think they handle them by default.
CloudFront is connected correctly (as it displays the images correctly when accessing outside the browser). And the relevant CORS configuration is on this behavior:
The 2 CORS related policies are the default ones included by CloudFront:
So in summary, everything should be being forwarded as expected. After each CloudFormation change, I create an invalidation for /*
, so nothing should be cached on CF side.
In this CORS test page I see it works as expected.
But when invoking it through my application I see the following error.
Access to fetch at 'https://images.toscanini.me/travel/europa-2023/barcelona/palau-musica-catalana-techo-del-teatro.avif' from origin 'https://toscanini.me' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
When I access it through cURL it works:
curl -I -H "Origin: https://toscanini.me" https://images.toscanini.me/travel/europa-2023/barcelona/palau-musica-catalana-techo-del-teatro.avif
Response:
HTTP/2 200
content-type: image/avif
content-length: 1062925
date: Thu, 27 Mar 2025 17:48:45 GMT
access-control-allow-origin: *
access-control-allow-methods: GET, HEAD
last-modified: Wed, 05 Feb 2025 12:47:36 GMT
etag: "3f4ca06ef766a9cb71e287490fc05aa9"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
x-cache: Hit from cloudfront
via: 1.1 90b76e653c2ec3d955102c951a3c0250.cloudfront.net (CloudFront)
x-amz-cf-pop: GRU1-C1
x-amz-cf-id: KvVQfT7aiMgJKHrtvFknyeimDWfD0ZkQJo_ktWaF3tlS_EThdlxnlg==
age: 21658
So I'm really lost on what's the error. I know it's probably a misconfiguration somewhere, but can't see where!
Your CloudFront configuration is already working well and supports CORS request.
It's the way you are loading the panorama viewer, remove the requestHeaders
and withCredentials
field, so your code becomes
panoramaViewer = new Viewer({
container: viewerElement,
panorama: imageURL,
});
The requestHeaders isn't doing anything in this case because both Sec-Fetch-Mode
and Origin
are forbidden request headers, meaning they can't be modified.
As for the withCredentials
, you don't need this, since you are not sending any credentials (Authorization
or Cookie
headers). Plus, having the Access-Control-Allow-Origin
set to *, will trigger an error making your request fail.
Failed to load resource: Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
Here is a working demo with the correct code showing the panorama