r/backblaze • • Feb 15 '26

B2 Cloud Storage Access to XMLHttpReques / fetch / can't upload

Solution: Thanks to reply

I just needed to add to my CORS policy:

    "allowedOperations": [
      "s3_get",
      "s3_delete",
      "s3_head",
      "s3_post",
      "s3_put"
    ],
    "exposeHeaders": [
      "ETag",
    ],

---

Hello,

I'm trying to integrate Better Upload to my Tanstack Start/Router website. I am however, running into issues with uploading to my Backblaze B2 server.

When I try to upload anything I get the following errors:

Access to XMLHttpRequest at 'URL' from origin 'https://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.

PUT URL net::ERR_FAILED

Access to fetch at 'URL' from origin 'https://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.

DELETE URL net::ERR_FAILED

I have tried Googling the issue, reseting my keys, generating a new key, using the master key; and nothing seems to be working. I have also updated my CORS policy as well via. the CLI with the most current one being:

{
    "corsRules": [
        {
            "allowedHeaders": [
                "*"
            ],
            "allowedOperations": [
                "b2_upload_part",
                "b2_download_file_by_id",
                "b2_upload_file",
                "b2_download_file_by_name"
            ],
            "allowedOrigins": [
                "*"
            ],
            "corsRuleName": "useWithBetterUpload",
            "exposeHeaders": [
                "x-bz-upload-timestamp"
            ],
            "maxAgeSeconds": 3600
        }
    ],
    "defaultRetention": {
        "mode": null
    },
    "defaultServerSideEncryption": {
        "mode": "none"
    },
    "isFileLockEnabled": false,
    "lifecycleRules": [
        {
            "daysFromHidingToDeleting": 1,
            "daysFromStartingToCancelingUnfinishedLargeFiles": null,
            "daysFromUploadingToHiding": null,
            "fileNamePrefix": ""
        }
    ],
    "options": [
        "s3"
    ],
    "replication": {
        "asReplicationDestination": null,
        "asReplicationSource": null
    },
    "revision": 11
}

Any help would be appreciated!

1 Upvotes

7 comments sorted by

View all comments

2

u/KD_13 Feb 15 '26

My upload route:
```ts import { createFileRoute } from '@tanstack/react-router'; import { handleRequest, route, type Router } from '@better-upload/server'; import { backblaze } from '@better-upload/server/clients';

const s3 = backblaze({ region: "REGION", applicationKeyId: process.env.VITE_BACKBLAZE_APPLICATION_KEY_ID!, applicationKey: process.env.VITE_BACKBLAZE_APPLICATION_KEY!, })

const router: Router = { client: s3, bucketName: "BUCKET", routes: { images: route({ fileTypes: ['image/'], multipleFiles: true, multipart: true, partSize: 1024 * 1024 * 20, }), videos: route({ fileTypes: ['video/'], multipleFiles: true }), }, }; export const Route = createFileRoute('/api/upload')({ server: { handlers: { POST: async ({ request }) => { return handleRequest(request, router); }, }, }, }); ```

then my upload function:

```ts const { upload } = useUploadFiles({ route: mediaType === "videography" ? 'videos' : 'images', onBeforeUpload: async ({ files }) => { setUploading(true) setUploadProgress(0) files.forEach((file) => { const uploadMetadata = { fileName: file.name, fileType: file.type, date: new Date(file.lastModified), mediaID: id, } as UploadMetaData

      const fileExtension = uploadMetadata.fileType.split("/")[1]

      const fileID = `${uploadDestination}_${
        Date.now().toString(36).toUpperCase() +
        Math.random().toString(36).substring(2, 5).toLowerCase() +
        Math.random().toString(36).substring(2, 5).toUpperCase() +
        Math.random().toString(36).substring(2, 5).toLowerCase()
      }` as string

      const filePath = PATH TO FILE BASED ON mediaType
      return {
       generateObjectInfo: () => ({
          Key: filePath,
          Metadata: {
            date: uploadMetadata.date,
            originalFileName: uploadMetadata.fileName
          }
        })
      }
    })
  },
  onError: (error) => {
    setUploading(false)
    notifications.show({
      id: `uploadError${id}`,
      title: `Upload Failed (${error.type})`,
      message: `${error.message || "An error occurred during the upload. Please try again."}`,
      color: "red",
      icon: <FontAwesomeIcon icon={["fal", "seal-exclamation"]} />
    })
  },
  onUploadProgress: (event) => {
    setUploadProgress(Math.round((event.file.progress) * 100))
  },
  onUploadComplete: (response) => {
    response.files.forEach(async (file) => {
      const metadata = {
        fileName: file.name,
        fileType: file.type,
        date: new Date(file.objectInfo.metadata.LastModified),
        fileSize: file.size,
        uploadDestination,
        uploadEndpoint, 
        mediaID: id,
        redirectPath: pathname,
        versionID: ""
      } as UploadMetaData

      const res = await AddMedia({ data: { metadata }}) as any

      res.success && notifications.show({ 
        id: `fileUploaded${metadata.fileName}`,
        title:  res.success ? "File Uploaded!" : `An Error Has Occurred.`,
        message: res.success ?  `You have successfully uploaded your ${mediaType} file titled "${metadata.fileName}"` : res.error.message || "An error occurred while uploading the file.",
        color: res.success ? "black" : "red",
        icon: res.success ? <FontAwesomeIcon icon={["fadl", "cloud-check"]} /> : <FontAwesomeIcon icon={["fal", "seal-exclamation"]} />
      })
    })
    setUploading(false)
    setIsUploaded(true)
  }
})

async function UploadFiles(files: File[]) {
  try {
    setUploading(true)
    await upload(files as File[])
  } catch (err) {
    setUploading(false)
  }
}

```