Presigned Posts in R2 storage

Hey there,

for an application I am making I require presigned posts in order to limit the file size. I basically want my users to upload their content via their browser (to minimize bandwidth on the production app-server), which requires control from the backend that generates the presigned post in order to block contents being uploaded that may try to abuse the API.

Is there any update regarding an implementation of presigned posts (more specifically via aws-sdk for JS “createPresignedPost”) using R2 storage? If not, this would be a dealbreaker since (as far as I know) you cannot really control the content-length nor type using the presigned URL safely.

If there’s a safe possibility that I don’t know of please let me know. :slight_smile:

Best regards

As far as I know, you can use presigned URLs to control content-length and content-type if you presign the headers.

For example, using aws4fetch:

const signedUrl = await r2.sign(`https://[ACCOUNT-ID].cloudflarestorage.com/[BUCKET]/${filename}`,
        {
            method: "PUT",
            aws: { signQuery: true, allHeaders: true },
            headers: {
                "x-amz-expires": 14400,
                "x-amz-meta-custom": "Custom value",
                "content-type": "image/jpeg",
                "content-length": contentLength
            },
        }
    );
1 Like

hey thanks for the reply, this is correct but signed URLs (PUT) could theoretically be bypassed from the client side by sending malicious headers that tell the server a certain length is given, even if it is untrue - afaik. I think this is primarily the reason why AWS docs even highlight that difference between POST and PUT requests - being that PUT requires the file to be uploaded to a custom backend that then uploads to the cloud storage as supposed the browser uploading directly to the cloud.

If I am incorrect here please correct me. :slight_smile:

This isn’t possible with R2.

Like the person above me said, when I tested this with forged headers, R2 eventually rejected the upload.