What am I missing? I don't see anything in the Filament Spatie Media Library Plugin docs that mentions this CORS issue. My R2 settings has set all origins to be allowed.
I haven't published any CORS settings in Laravel, instead keeping them default.
I cannot upload images in Filament with Filament Spatie Media Upload Plugin, it's always giving me a 403 CORS Missing Allow Origin.
This is the plugin, https://filamentphp.com/plugins/filament-spatie-media-library, and the only thing it mentions for using private uploads is setting the visibility to private.
I can confirm this isn't an issue with setting up R2 on my Laravel project, following, https://www.luckymedia.dev/blog/integrating-cloudflare-r2-storage-with-laravel. Nor is it an issue with Spatie Media LIbrary, as I can run that in PHP Artisan Tinker and it'd be uploaded.
// Put example file into r2, the default, storage. Works fine
Storage::disk()->put('test.txt', 'test');
// Add url image to model and test spatie media library upload
// Works all fine
$url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/PHP-logo.svg/711px-PHP-logo.svg.png';
$model = new BasicModel([
'name' => 'coolImage',
'icon' => $url,
'user_id' => '0195ef2e-uuid-here'
]);
try {
$model->addMediaFromUrl($url)->toMediaCollection();
} catch (Exception $e) {
Log::error($e->getMessage());
}
$model->save();
With the relevant .env values
FILESYSTEM_DISK=r2
MEDIA_DISK=r2
FILAMENT_FILESYSTEM_DISK=r2
R2_ACCESS_KEY_ID=123
R2_SECRET_ACCESS_KEY=123
R2_BUCKET=test-cf-bucket
R2_ENDPOINT=https://123.r2.cloudflarestorage.com
config/filesystems
'r2' => [
'driver' => 's3',
'key' => env('R2_ACCESS_KEY_ID'),
'secret' => env('R2_SECRET_ACCESS_KEY'),
'region' => 'auto',
'bucket' => env('R2_BUCKET'),
'url' => env('R2_URL'),
'endpoint' => env('R2_ENDPOINT'),
'use_path_style_endpoint' => env('R2_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
],
My Form is not using any crazy settings, only the plugin and setting visibility to private.
BasicFormResource.php
Forms\Components\SpatieMediaLibraryFileUpload::make('image')
->image()
->visibility('private')
->maxFiles(1),
In R2 CORS Settings, I'm using a very open permission to test it works in localhost
[
{
"AllowedOrigins": [
"*"
],
"AllowedMethods": [
"GET",
"PUT",
"POST",
"DELETE",
"HEAD"
],
"AllowedHeaders": [],
"ExposeHeaders": []
}
]
The XML file from the CORS error says to check my secret access key and signing method,
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your secret access key and signing method.
</Message>
<CanonicalRequest>
GET /livewire-tmp/MKLfjkllkjvmetaUEhQLWxvZ28uc3ZnLnBuZw%3D%3D-.png X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=12jkfj12klj18fkls%2F20250403%2Fauto%2Fs3%2Faws4_request&X-Amz-Date=20250101T010111Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host%3Bx-amz-acl&x-amz-acl=private host:test-cf.r2.cloudflarestorage.com x-amz-acl: host;x-amz-acl UNSIGNED-PAYLOAD
</CanonicalRequest>
So I allowed all headers and then it worked. Here's the final output of my R2 CORS settings incase someone else gets stuck on this. Note the allowed origin should be changed to your prod url after localhost testing is done.
[
{
"AllowedOrigins": [
"http://127.0.0.1:8000"
],
"AllowedMethods": [
"GET",
"PUT",
"POST",
"DELETE",
"HEAD"
],
"AllowedHeaders": [
"*"
],
"ExposeHeaders": []
}
]
Please or to participate in this conversation.