Cloudflare Images direct upload CORS problem

I’m trying to upload a file from the browser using the direct_upload endpoint which returns an upload URL.

Unfortunately when trying to upload via XHR from the browser I’m getting this strange CORS error:

Access to XMLHttpRequest at 'https://upload.imagedelivery.net/....' from origin 'https://127.0.0.1:5555' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.

The JS code that uploads is essentially this with some added event handlers:

xhr.open('POST', url);
xhr.send(file);

The file is obtained from an <input type="file"> element.

I’m using XHR instead of fetch because I need to monitor the upload progress to be able to display a progress bar. Fetch doesn’t have support for monitoring upload progress. See these two issues for more info about this:

https://github.com/github/fetch/issues/89

https://github.com/whatwg/fetch/issues/607

Check out these previous posts

1 Like

Thanks. I had seen those posts but since the only solution involved using fetch I discarded it.

Looking at it in more detail the solution was to send multipart/form-data instead of the straight file with content type image/jpeg.

So I did:

const formData = new FormData();
formData.append('file', file);
xhr.send(formData);

And that finally worked.

Now I understand the issue. The server CORS settings don’t allow any other value for the content-type header than multipart/form-data. Not the best way to communicate an error with a user if you ask me.

It’s really confusing the docs don’t clarify this. The only example is a cryptic HTML form.

It was very, very painful to get it to work! I tried using axios, which is the lib I use for all my api requests and could not get it to work with axios. Eventually I used fetch and it worked - who knows why.

Here’s the code I’m currently using:

const formData = new FormData()
formData.append('file', actualFile, 'filename')

const uploadResult = await fetch(uploadUrl, {
    method: 'post',
     body: formData
})
const result = await uploadResult.json()

I hope this spares you some time.

What “got” me was that I looked at the example on Direct Creator Upload · Cloudflare Image Optimization docs and I named the parameter I appended “myFile”. After changing the name from “myFile” to “file” it worked fine. I wish the error returned was “Bad Request: Missing parameter named ‘file’” instead of “Bad request: Error parsing form fields.” That would have saved me a couple of hours of frustration.

2 Likes

Hey! I’m Florian - an engineer on the Images team. I’ve had a look at the issue.

I can see two issues here that make working with the direct-creator-upload harder than it needs to be:

  • The CORS error seems unpredictable (you can read below when and why you see it)
  • We expect the name of the uploaded image to be ‘file’. When a user assigns a different name (we don’t accept different names) the error message is not helpful enough.

TL;DR;
Please ensure that your request uses the correct encoding which is multipart/form-data.

We are working on getting rid of the CORS error (expect a release next week). And we will make it more clear that the name of the parameter must be file (expect a change in the docs and/or an improved error message).

Why do you see this error and how to fix it?
As others have already mentioned, we expect the format of the request to be encoded as multipart/form-data. If you do so, you “should” have no problems making the upload request.

I’m saying “should” since most fetching libraries follow the fetch-spec and don’t send an OPTIONS (preflight) request before making a request encoded as multipart/form-data.

The CORS error arrises when the browser (or your fetching library) makes a preflight request to the server. This is always the case when your request contains the header Content-Type: application/json (this is the wrong encoding for our endpoint).

Our server previously did not return the Access-Control-Allow-Headers header when it received an OPTIONS request. This header is required for the browser to allow the actual request. If it is not present, your browser will show a CORS error.

In the future, we will return the Access-Control-Allow-Headers header from the endpoint, getting rid of the CORS error. This means that you can make a request even if your fetching library or browser will send a preflight request. This does not mean, we accept other encodings than multipart/form-data.

I’m sorry if you have been affected by this issue and thank you for reporting it back to us.

Update (03/22/2023): The changes are now live.

2 Likes

Hello @florian9.
Thanks for all the helpful information you provided earlier. I am attempting to use Direct Upload Creator, but I’m still facing issues. Perhaps you can help me.
My code looks like this:

const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/images/v2/direct_upload`;

const formData = new FormData()
formData.append('file', file)

const uploadResult = await fetch(url, {
  headers: {
    "Content-Type": "multipart/form-data",     
  },
  method: "POST",
  body: formData
})

No preflight request is being sent and I’m not getting CORS error anymore, but I do have a 400 error with no response message so I’m not really sure what I’m doing wrong. Do you know if I’m missing something?

Thanks in advance!!

const url = https://api.cloudflare.com/client/v4/accounts/${accountId}/images/v2/direct_upload;

The api request is for creating a special URL, which later can be used to upload an image to.

API docs: Cloudflare API Documentation

Explanation: Direct Creator Upload · Cloudflare Image Optimization docs

Hi @asemiglazov. Thank you for your reply.

Yeah, my understanding is that the response from a request to https://api.cloudflare.com/client/v4/accounts/${accountId}/images/v2/direct_upload;, will provide an uploadURL to upload the image. But I’m unable to get a response from that endpoint since I’m getting errors.

Should I be using a different URL to use the Direct Creator Upload? The links you provided have the same URL i’m using.

From the example you provided, You seem to trying upload an image data into https://api.cloudflare.com/client/v4/accounts/${accountId}/images/v2/direct_upload.

Instead, the body can’t be empty or has following data Cloudflare API Documentation

  --form expiry= \
  --form id= \
  --form metadata= \
  --form requireSignedURLs=
1 Like

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.