Are you using a custom domain or the r2.dev domain?
Are you using a custom domain or the r2.dev domain?

bin.føø.com (bin.xn--f-5gaa.com).somefile#123.jpg and I upload it via R2 dashboard, it shows as being uploaded as somefile, but accessing it via a URL encoded string (needed since anything after # isn't sent to the server by the browser). So the R2 upload process drops anything after the # (same as a browser would do making the request for it). At which point you would be left with an empty file.somefile#123.jpg ended up as somefile in the dashboard. I was too scared to try it with a file that started with # because I didn't want to end up an object that wasn't deletable. # in it, so it's not the only place in the world that works that way, but uploading to R2 isn't a normal browser request. Might be something like they are parsing the uploaded R2 object key as a URL path, and if it's a URL path, anything after # would be dropped. It does seem like a bug to me.PUT https://{account_id}.r2.cloudflarestorage.com/{bucketname}/{object_key}# isn't valid in an HTTP request.public function createR2Bucket($accountId, $bucketName)
{
$params = [
'name' => $bucketName,
];
return $this->makeRequest('POST', sprintf('accounts/%s/r2/buckets', $accountId), ['json' => $params]);
}https://{account_id}.r2.cloudflarestorage.com/{bucketname}/, no. The S3 API does not work on custom public domains for buckets (so for example you can't have a presigned URL within your own domain)bin.føø.com (bin.xn--f-5gaa.com)somefile#123.jpgsomefile#123.jpgsomefilesomefilePUT https://{account_id}.r2.cloudflarestorage.com/{bucketname}/{object_key}public function createR2Bucket($accountId, $bucketName)
{
$params = [
'name' => $bucketName,
];
return $this->makeRequest('POST', sprintf('accounts/%s/r2/buckets', $accountId), ['json' => $params]);
}https://{account_id}.r2.cloudflarestorage.com/{bucketname}/