When uploading files to S3 using presigned URLs from the frontend, you need to configure CORS on the S3 bucket to allow cross-origin requests.
Add the following CORS configuration to your S3 bucket (befitbecool):
- Go to AWS S3 Console
- Select your bucket:
befitbecool - Go to the "Permissions" tab
- Scroll to "Cross-origin resource sharing (CORS)"
- Click "Edit"
- Add the following JSON configuration:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET",
"PUT",
"POST",
"DELETE",
"HEAD"
],
"AllowedOrigins": [
"http://localhost:5173",
"http://localhost:3000",
"https://yourdomain.com"
],
"ExposeHeaders": [
"ETag",
"x-amz-server-side-encryption",
"x-amz-request-id",
"x-amz-id-2"
],
"MaxAgeSeconds": 3000
}
]# Save CORS configuration to a file
cat > cors-config.json << 'EOF'
{
"CORSRules": [
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
"AllowedOrigins": [
"http://localhost:5173",
"http://localhost:3000",
"https://yourdomain.com"
],
"ExposeHeaders": [
"ETag",
"x-amz-server-side-encryption",
"x-amz-request-id",
"x-amz-id-2"
],
"MaxAgeSeconds": 3000
}
]
}
EOF
# Apply CORS configuration
aws s3api put-bucket-cors --bucket befitbecool --cors-configuration file://cors-config.jsonresource "aws_s3_bucket_cors_configuration" "bucket_cors" {
bucket = aws_s3_bucket.befitbecool.id
cors_rule {
allowed_headers = ["*"]
allowed_methods = ["GET", "PUT", "POST", "DELETE", "HEAD"]
allowed_origins = [
"http://localhost:5173",
"http://localhost:3000",
"https://yourdomain.com"
]
expose_headers = [
"ETag",
"x-amz-server-side-encryption",
"x-amz-request-id",
"x-amz-id-2"
]
max_age_seconds = 3000
}
}- AllowedHeaders:
["*"]allows all headers (needed for presigned URLs) - AllowedMethods:
["GET", "PUT", "POST", "DELETE", "HEAD"]allows all necessary HTTP methods - AllowedOrigins: List of origins that can access the bucket
http://localhost:5173- Vite dev server (frontend)http://localhost:3000- Backend API (if needed)- Add your production domain(s) when deploying
- ExposeHeaders: Headers that browsers can access in responses
- MaxAgeSeconds: How long browsers should cache the CORS preflight response
The configuration above is suitable for development with wildcards for convenience.
-
Replace localhost origins with your actual production domains:
"AllowedOrigins": [ "https://yourdomain.com", "https://www.yourdomain.com" ]
-
Consider restricting AllowedHeaders if you know exactly which headers are needed:
"AllowedHeaders": [ "Content-Type", "Content-Length", "x-amz-*" ]
-
Restrict AllowedMethods to only what's needed:
"AllowedMethods": ["GET", "PUT"]
After applying the configuration, test it:
# Test preflight request
curl -X OPTIONS \
-H "Origin: http://localhost:5173" \
-H "Access-Control-Request-Method: PUT" \
-H "Access-Control-Request-Headers: Content-Type" \
-v \
"https://befitbecool.s3.us-east-1.amazonaws.com/"Expected response headers:
Access-Control-Allow-Origin: http://localhost:5173Access-Control-Allow-Methods: GET, PUT, POST, DELETE, HEADAccess-Control-Allow-Headers: *
- Clear browser cache - CORS preflight responses are cached
- Verify bucket name - Ensure you're applying CORS to the correct bucket
- Check AWS region - Make sure the bucket region matches your configuration
- Wait a few minutes - CORS configuration changes can take a moment to propagate
- Check bucket policy - Ensure your bucket policy doesn't block the uploads
aws s3api get-bucket-cors --bucket befitbecoolIf you cannot modify S3 CORS settings, you can proxy uploads through your backend:
- Frontend sends file to backend API
- Backend uploads to S3 server-side
- No CORS issues since it's server-to-server
However, this approach:
- ❌ Increases backend bandwidth usage
- ❌ Slower (two-hop upload)
- ❌ Higher backend resource usage
- ✅ No CORS configuration needed
- ✅ More control over uploads
- ✅ Better for validation/virus scanning
Development: Use presigned URLs with permissive CORS (as configured above)
Production:
- Use presigned URLs with restricted CORS (specific origins only)
- Consider adding CloudFront in front of S3 for better CORS control
- Implement rate limiting on presigned URL generation
- Monitor S3 access logs for abuse