Pre-signed URLs & CORS on Cloudflare R2
Cloudflare R2 recently added support for pre-signed URLs. These can be used to generate a temporary link for granting public read or write access to a bucket.
A great use case for a pre-signed URL is to support client-side uploads. You can fetch the short lived pre-signed URL on the client, generate it on a server (or lambda) using your Cloudflare credentials, and then use it as a PUT request to securely upload a file to R2.
Given R2’s infancy the documentation around pre-signed URLs and in particular CORS is still a work in progress. One of the likely (and very frustrating!) errors you will run into is:
Access to fetch at … 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.
In order to get pre-signed URLs to work on the client, we first need to set up Cross-Origin Resource Sharing (CORS). On AWS this can be configured through the console or programmatically. However, currently the only way to configure CORS on R2 is programatically. R2 is S3 compatible-ish and the relevant API that has been implemented in R2 is PutBucketCORS.
You want to set your CORS configuration to look something like this:
AllowedHeaders: [“content-type”], // Don’t use * — it won’t work!
AllowedOrigins: [“*”] // You can make this more specific
The trick here is to set AllowedHeaders to “content-type”. In most S3 examples this is commonly noted as “*”. This works fine on AWS but, at the time of writing, does not work on R2. There’s some discussion in the Cloudflare Discord that this may be a bug — so hopefully it has been resolved by the time you are reading this. I’ll also try to update this post if the situation changes.
If you need to check the current CORS configuration for a bucket, you can do so via the GetBucketCORS API.