Uploading file to DigitalOcean Spaces, get "Access to XMLHttpRequest at (url) from origin (url) has been blocked by CORS policy"

Viewed 3942

The complete error in the console is

Access to XMLHttpRequest at (imageurl) from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I tried setting the CORS Configuration to allow PUT and POST on all domains, but still get the error.

The code on the client side looks like this -

// digitalOcean.js
import AWS from 'aws-sdk'

const regionName = 'nyc3'
const accessKeyId = 'KEYID'
const accessSecretKey = 'SECRETKEY'
export const bucketName = 'BUCKETNAME'

const endpointUrl = `${regionName}.digitaloceanspaces.com`
export const bucketUrl = `https://${bucketName}.${endpointUrl}/`

const spacesEndpoint = new AWS.Endpoint(endpointUrl)
export const s3 = new AWS.S3({
  endpoint: spacesEndpoint,
  accessKeyId: accessKeyId,
  secretAccessKey: accessSecretKey,
})

and

// getItem.jsx
import * as digitalOcean from '../services/digitalOcean'

function uploadFile(file) {
  const params = { Body: file, Bucket: digitalOcean.bucketName, Key: file.name }
  digitalOcean.s3.putObject(params)
    .on('build', request => {
      request.httpRequest.headers.Host = digitalOcean.bucketUrl
      request.httpRequest.headers['Content-Length'] = file.size
      request.httpRequest.headers['Content-Type'] = file.type
      request.httpRequest.headers['x-amz-acl'] = 'public-read'
    })
    .send((err, data) => {
      if (err) return alert(JSON.stringify(err))
      // ...
    })
}

Any idea what the problem might be?

3 Answers

I eventually got it working by setting the Allowed Headers to * also, like so -

screenshot1 screenshot2

Writing this in case it's helpful for future readers:

I was struggling with a similar issue, where we used pre-signed URLs with the DigitalOcean spaces storage backend for multi-part file uploads.

Initially, the requests failed entirely due to lacking CORS policies in the DigitalOcean spaces responses. After some diggin, it turned out you can configure CORS policies in the DO spaces management dashboard.

Next issue we ran to is that the client (browser) didn't have access to the ETag response header sent by DigitalOcean spaces, as it was lacking the Access-Control-Expose-Headers header allowing the access to the ETag header. To solve this, there doesn't seem to be an official DigitalOcean supported way to do it, but luckily I found this thread which showed how you can do it directly with s3cmd:

With s3cmd, it's possible to upload a CORS policy file directly to the bucket, which also works on DigitalOcean spaces. This overrides any existing CORS settings set on the bucket

Example CORS policy file:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <ExposeHeader>ETag</ExposeHeader>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Note how we can define the ExposeHeader property here on top of the settings already manageable on the DigitalOcean dashboard. I do not know why has the option been omitted from the official UI.

Once you have the file and s3cmd configured, it's simple enough to upload to the bucket:

s3cmd setcors FILE s3://BUCKET

I had the samme issue, but I am creating buckets dynamically and need to be able to set CORS from nodejs. The way I solved it was to wrap the putBucketCors AWS S3 function, and use that for updating CORS after I have created a new bucket/space.

const spacesEndpoint = new AWS.Endpoint('ams3.digitaloceanspaces.com');
const s3 = new AWS.S3({
    endpoint: spacesEndpoint,
    accessKeyId: 'ACCES_KEY',
    secretAccessKey: 'SECRET',

});

const setCors = (bucket: string) => {
    return new Promise<{}>((resolve, reject) => {
        s3.putBucketCors({
            Bucket: bucket,
            CORSConfiguration: {
                CORSRules: [{
                    AllowedOrigins: ['*'],
                    AllowedMethods: ['GET']
                }]
            }
        },
            (err, data) => err ? reject(err) : resolve(data)
        );
    });
}
Related