amazon-web-servicesamazon-s3corsamazon-cloudfront

CORS Issue with CloudFront + S3


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.

Details

Client

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.

Server

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: cloudfront behavior

The 2 CORS related policies are the default ones included by CloudFront:

CORS-S3Origin: enter image description here

Managed-SimpleCORS: enter image description here

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!


Solution

  • 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