By sgrund14
Hello,
So I’m trying to upload to Spaces using the AWS s3 presigned URL SDK, but I’m getting a CORS error whenever I set the ACL to public read.
// generates URL without "x-amz-acl=public-read"
// does not throw CORS error, but uploads a private file that blocks public access
const s3Params = {
Bucket: process.env.UploadBucket,
Key,
Expires: URL_EXPIRATION_SECONDS,
ContentType: decodeURIComponent(mimeType)
}
const uploadURL = await s3.getSignedUrlPromise('putObject', s3Params)
// generates URL with "x-amz-acl=public-read" query string parameter
// throws CORS error when I try to PUT an object there
const s3Params = {
Bucket: process.env.UploadBucket,
Key,
Expires: URL_EXPIRATION_SECONDS,
ContentType: decodeURIComponent(mimeType),
ACL: 'public-read'
}
const uploadURL = await s3.getSignedUrlPromise('putObject', s3Params)
In my spaces config, I have Allowed Headers set to “*”, so I’m not sure what is causing the CORS error. Any ideas?
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Hi there,
From what I have seen, this is expected behavior with DigitalOcean Spaces and presigned URLs. When you add ACL: 'public-read', the presigned URL typically expects the x-amz-acl header to be part of the request. From the browser side, that usually turns the upload into a non-simple CORS request, which then triggers a preflight OPTIONS check.
Even if your CORS config allows *, in practice you may still need to explicitly allow x-amz-acl in the Spaces CORS settings and make sure PUT and OPTIONS are listed as allowed methods. Otherwise the browser can block the upload before it reaches Spaces.
Two approaches that often work:
You can keep using public-read, but update your Spaces CORS config to explicitly include x-amz-acl in Allowed Headers.
Or you can upload the object as private (no ACL in the presigned URL) and then make it public afterward from the server side or via a public bucket. That tends to avoid most CORS edge cases in the browser.
So while there could be a few moving parts here, depending on the client and SDK, the x-amz-acl header requirement is usually the main trigger for this kind of CORS error.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Full documentation for every DigitalOcean product.
The Wave has everything you need to know about building a business, from raising funding to marketing your product.
Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.
New accounts only. By submitting your email you agree to our Privacy Policy
Scale up as you grow — whether you're running one virtual machine or ten thousand.
From GPU-powered inference and Kubernetes to managed databases and storage, get everything you need to build, scale, and deploy intelligent applications.