My situation

Lambda+API Gateway function to fetch S3 image and resize if query params are present. For example /path/to/jake.jpg responds with the image directly from S3, whereas /path/to/jake.jpg?width=200 responds with a resized version of the image.

This is then all fronted with Cloudfront to optimize performance for an abundance of images.

So I want to tell my Amplify app to give me the Cloudfront URL of the image, but I want to make sure that it’s present in Cloudfront first, so I need to do a quick check if the resized image exists yet in S3, and if not quickly generate it and return the URL of the new image.

Despite configuring CORS on my API Gateway API and ensuring countless times I was returning the proper Access-Control-Allow-Origin header in my response, I continued getting the infamous No Access-Control-Allow-Origin Header on resource

Ultimately my solution was actually the CORS policy on the S3 bucket!

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <ExposeHeader>x-amz-server-side-encryption</ExposeHeader>
    <ExposeHeader>x-amz-request-id</ExposeHeader>
    <ExposeHeader>x-amz-id-2</ExposeHeader>
    <ExposeHeader>ETag</ExposeHeader>
    <ExposeHeader>Access-Control-Allow-Origin</ExposeHeader>
    <ExposeHeader>Access-Control-Allow-Methods</ExposeHeader>
    <ExposeHeader>Access-Control-Allow-Credentials</ExposeHeader>
    <ExposeHeader>Access-Control-Allow-Headers</ExposeHeader>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

What I discovered after an embarrassing amount of time was the missing headers from the S3 policy:

<ExposeHeader>Access-Control-Allow-Origin</ExposeHeader>
<ExposeHeader>Access-Control-Allow-Methods</ExposeHeader>
<ExposeHeader>Access-Control-Allow-Credentials</ExposeHeader>
<ExposeHeader>Access-Control-Allow-Headers</ExposeHeader>