I'm serving static content from an S3 bucket via a signed url from Cloudfront that is redirected from my REST API using a 302. I'm trying to retrieve that content via my frontend web app and I'm running into CORS issues related to missing access-control-*
headers in my GET request for the asset.
When making a request the initial OPTIONS
request returns the expected headers:
Status: 200
access-control-allow-credentials: true
access-control-allow-headers: x-client-version
access-control-allow-methods: GET, HEAD
access-control-allow-origin: https://subdomain.version_number.domain.extension
access-control-expose-headers: Access-Control-Allow-Origin
However the following GET
request is missing any of the same Access-Control-Allow-Credentials
, Access-Control-Allow-Headers
etc are completely missing from the GET
request and the console shows:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at [signed_url] (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
Curl works but the browser doesn't
Using curl
and setting the correct origin
I can see the correct Access-Control-*
headers being set:
curl -H "Origin: https://subdomain.version_number.domain.extension" --verbose "[signed_url]"
Response:
> Host: mybucket.anotherdomain.com
> User-Agent: curl/8.5.0
> Accept: */*
> Origin: https://subdomain.version_number.domain.extension
> Access-Control-Request-Method: GET
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/2 200
< content-length: 0
< date: Fri, 21 Jun 2024 15:22:50 GMT
< access-control-allow-origin: https://subdomain.version_number.domain.extension
< access-control-allow-methods: GET, HEAD
< access-control-expose-headers: Access-Control-Allow-Origin
< access-control-allow-credentials: true
< vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
< server: AmazonS3
< x-cache: Miss from cloudfront
< via: 1.1 [].cloudfront.net (CloudFront)
< x-amz-cf-pop: []
< x-amz-cf-id: []
I've configured the Cloudfront distribution using the following:
Origin and origin groups: my_s3_bucket
Viewer protocol policy: HTTPS only
Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
Restrict viewer access: Yes -> trusted key group assigned to the bucket
Cache key and origin requests policies
The S3 bucket CORS config is set to:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET",
"HEAD"
],
"AllowedOrigins": [
"https://subdomain.version_number.domain.extension"
],
"ExposeHeaders": [
"Access-Control-Allow-Origin"
]
}
]
Cloudfront -> Bucket access policy
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity[ID]"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my_s3_bucket/*"
}
]
}
It turns out that performing a 302 on a get request with credentials: true
is not supported by browsers:
Chrome: https://issues.chromium.org/issues/40290910
Mozilla Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1346749
According to the Mozilla docs this approach of redirecting traffic with credentials is not recommended and should be refactored:
The CORS request was responded to by the server with an HTTP redirect to a URL on a different origin than the original request, which is not permitted during CORS requests.
For example, if the page https://service.tld/fetchdata were requested, and the HTTP response is "301 Moved Permanently", "307 Temporary Redirect", or "308 Permanent Redirect" with a Location of https://anotherservice.net/getdata, the CORS request will fail in this manner.
To fix the problem, update your code to use the new URL as reported by the redirect, thereby avoiding the redirect.
Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSExternalRedirectNotAllowed