Cloudflare Images CORS error


I have used the Direct Creator Upload Url in postman to upload an image. Which is fine since the images uploades successfully to Cloudflare images.

However, when I send the fetch request from the client side, I get CORS error.

Below is the error I am getting

Please help

Aruneel Das

1 Like

Check out this post. They were able to work around it.

There are other topics on this


As per the solution given, they are making an initial request to Cloudflare for the uploadURL (using axios)

However, it is the initial request that is giving me the CORS error. I am using fetch. I tried using axios too. But the same issue persists.

Hey @aruneel.das,

I posted something similar here and here.

This problem me hours of pain (so frustrating)! Here’s a function I created that works for me - I hope it helps:

async uploadToCloudFlare(uploadUrl, fileObj) {
  const formData = new FormData()
  formData.append('file', fileObj.file,

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

The problem is getting the upload URL in the first place. I have no problem getting it from the server backend (via PHP CURL), but if I try get it using fetch or XHR, I get the same CORS error.

This is the fetch code I’m using:

function getUploadURL() {
    const body = new FormData;
    body.append("requireSignedURLs", "false");
    body.append("", "\\");
    body.append("metadata", "{\"copyright\":\"name\"}");

    fetch("<ACCOUNT-ID-HIDDEN>/images/v2/direct_upload", {
      headers: {
        Authorization: "Bearer <API-KEY-HIDDEN>",
        "Content-Type": "multipart/form-data"
      method: "POST"
    .then((response) => response.json())
    .then((data) => console.log(data))
    .catch(() => { console.log("ERROR") })

Which returns this error:
Access to fetch at '' from origin '<my website url>' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

The upload form I’m creating is entirely AJAX, so ideally, I should be able to generate new upload URLs as needed, without reloading the page.

My current ducktape solution is to pre-generate a large amount of upload URLs via PHP on page load and then use them up as needed, but this creates a huge overhead and lengthy pageload times for what might not even get used.
The only other method I can think of is some hacky solution involving fetching a PHP page on my server that gets the key and then returns it, but that too has a lot of unnecessary overhead for what should be a simple, direct API call.

I might be misunderstanding you, so forgive me if I am…

You shouldn’t be loading your api keys to your gui for security reasons, so the call that generates your upload url should definitely be on your server.

With that in mind, if you create an endpoint/function in your PHP code that makes the request to Cloudflare to generate the url and return the url to your gui, it means that you can create the urls on demand, as needed, and your keys stay safe.

The issue I was having was that once I had the upload url, I was getting cors issues and the above solution, using fetch, is what ended up working for me.

1 Like

You’re absolutely right, that was a huge oversight on my part.

Here’s the gist of what I ended up using, in case it’ll help anyone else down the line.

The separate PHP file for communicating with the Cloudflare API:

  ///Session check to ensure the request is coming from an authenticated user
  ///You probably don't want to copy/paste this part
  if (isset($_SESSION['logged_in']) == false || $_SESSION['logged_in'] != true) {
    echo '{"success": false, "errors": [{"code": "internal", "message": "CP AUTH FAILED - Reload the page and try again."}]}';

  $accountID = "<ID HIDDEN>";
  $authToken = "<TOKEN HIDDEN>";

  $ch = curl_init();

  curl_setopt($ch, CURLOPT_URL, "$accountID/images/v2/direct_upload");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($ch, CURLOPT_POST, 1);
  $post = array(
      'requireSignedURLs' => 'true',
      'metadata' => '{"copyright":"NameHere"}'
  curl_setopt($ch, CURLOPT_POSTFIELDS, $post);

  $headers = array();
  $headers[] = "Authorization: Bearer $authToken";
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

  $result = curl_exec($ch);


  if (curl_errno($ch)) {
    echo '{"success": false, "errors": [{"code": "internal", "message": '.curl_errno($ch).'}]}';
  else if ($result) {
    echo $result;
  else {
    echo '{"success": false, "errors": [{"code": "internal", "message": "NO RESPONSE - Cloudflare failed to respond."}]}';


The Javascript function for obtaining an upload URL asynchronously, using promises.

  function getUploadURL() {
    ///The path to the PHP script that communicates with Cloudflare's API
    var url = "fetchUrl.php";

    return new Promise(function (resolve, reject) {
      let xhr = new XMLHttpRequest();"POST", url);
      xhr.onload = function () {
          if (this.status >= 200 && this.status < 300) {
              if (JSON.parse(xhr.response).success === true) {
              else {
                    status: this.status,
                    statusText: xhr.statusText,
                    error: JSON.parse(xhr.response).errors[0]
          } else {
                status: this.status,
                statusText: xhr.statusText;
      xhr.onerror = function () {
              status: this.status,
              statusText: xhr.statusText


A quick example of how to use the function.

  async function uploadFileExample() {
    let curlObj = "";
    try {
      curlObj = await getUploadURL(); //Waits for the getUploadURL function to return
    catch (e) { //Abort if error is thrown
    curlObj = JSON.parse(curlObj); //Convert the JSON string into a JS object
    let url = curlObj.result.uploadURL; //Extract the returned upload URL
    ///The rest of your upload code goes under here

This topic was automatically closed after 14 days. New replies are no longer allowed.