Introduction
Cross-Origin Resource Sharing (CORS) allows web applications from one domain to access S3 resources in another domain. Without proper CORS configuration, browsers block these requests, causing applications to fail when loading fonts, images, or data from S3.
Symptoms
Browser console error:
Access to fetch at 'https://my-bucket.s3.amazonaws.com/data.json' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.Network error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resourcePreflight failure:
Response to preflight request doesn't pass access control check: It does not have HTTP ok status.Common Causes
- 1.No CORS configuration - Bucket has no CORS rules defined
- 2.AllowedOrigins missing origin - Rule doesn't include requesting domain
- 3.AllowedMethods incorrect - Wrong HTTP methods configured
- 4.Preflight (OPTIONS) not handled - Missing OPTIONS method or headers
- 5.Headers mismatch - AllowedHeaders doesn't include request headers
- 6.Multiple CORS rules conflict - Conflicting rule configurations
- 7.CORS caching - Browser caching old CORS response
Step-by-Step Fix
- 1.Check logs for specific error messages
- 2.Verify configuration settings
- 3.Test network connectivity
- 4.Review recent changes
- 5.Apply corrective action
- 6.Verify the fix
Step 1: Check Current CORS Configuration
```bash # Get bucket CORS configuration aws s3api get-bucket-cors --bucket my-bucket
# If empty or error, no CORS configured # An error occurred (NoSuchCORSConfiguration) when calling the GetBucketCors operation: The CORS configuration does not exist ```
Step 2: Configure Basic CORS Rule
```bash # Add CORS configuration aws s3api put-bucket-cors --bucket my-bucket \ --cors-configuration '{ "CORSRules": [ { "AllowedOrigins": ["https://example.com"], "AllowedMethods": ["GET", "HEAD"], "AllowedHeaders": ["*"], "MaxAgeSeconds": 3000, "ExposeHeaders": ["ETag", "x-amz-request-id"] } ] }'
# Verify configuration aws s3api get-bucket-cors --bucket my-bucket ```
Step 3: Configure CORS for Multiple Origins
```bash aws s3api put-bucket-cors --bucket my-bucket \ --cors-configuration '{ "CORSRules": [ { "AllowedOrigins": [ "https://example.com", "https://app.example.com", "https://staging.example.com" ], "AllowedMethods": ["GET", "HEAD", "PUT", "POST", "DELETE"], "AllowedHeaders": ["*"], "MaxAgeSeconds": 3000, "ExposeHeaders": ["ETag"] } ] }'
# Or allow all origins (use cautiously) aws s3api put-bucket-cors --bucket my-bucket \ --cors-configuration '{ "CORSRules": [ { "AllowedOrigins": ["*"], "AllowedMethods": ["GET", "HEAD"], "AllowedHeaders": ["*"], "MaxAgeSeconds": 3000 } ] }' ```
Step 4: Handle Preflight Requests
Browsers send OPTIONS preflight for: - PUT, POST, DELETE methods - Custom headers - Content-Type other than application/x-www-form-urlencoded
aws s3api put-bucket-cors --bucket my-bucket \
--cors-configuration '{
"CORSRules": [
{
"AllowedOrigins": ["https://example.com"],
"AllowedMethods": ["GET", "HEAD", "PUT", "POST", "DELETE", "OPTIONS"],
"AllowedHeaders": [
"Authorization",
"Content-Type",
"Content-MD5",
"X-Amz-Date",
"X-Amz-Content-Sha256"
],
"MaxAgeSeconds": 3000,
"ExposeHeaders": ["ETag"]
}
]
}'Step 5: Test CORS Configuration
```bash # Test with curl (preflight request) curl -X OPTIONS \ -H "Origin: https://example.com" \ -H "Access-Control-Request-Method: GET" \ -H "Access-Control-Request-Headers: Authorization" \ -i https://my-bucket.s3.amazonaws.com/object.txt
# Response should include: # Access-Control-Allow-Origin: https://example.com # Access-Control-Allow-Methods: GET, HEAD # Access-Control-Allow-Headers: Authorization
# Test actual GET request curl -H "Origin: https://example.com" \ -i https://my-bucket.s3.amazonaws.com/object.txt ```
Step 6: Check for Common CORS Issues
```bash # Issue 1: Missing headers in AllowedHeaders # If request has Authorization header, it must be in AllowedHeaders
# Issue 2: Wrong AllowedOrigins # Must match exact origin including protocol (https://) # Wildcard (*) works but is less secure
# Issue 3: Method not allowed # Ensure all methods used by application are in AllowedMethods
# Check exact request being made # Browser dev tools -> Network tab -> inspect request headers ```
Step 7: Configure for S3 Static Website
For S3 static website hosting:
```bash aws s3api put-bucket-cors --bucket my-bucket \ --cors-configuration '{ "CORSRules": [ { "AllowedOrigins": ["*"], "AllowedMethods": ["GET", "HEAD"], "AllowedHeaders": ["*"], "MaxAgeSeconds": 3000 } ] }'
# Note: S3 website endpoint vs REST API endpoint behave differently # Website: http://my-bucket.s3-website.region.amazonaws.com # REST: https://my-bucket.s3.region.amazonaws.com ```
Step 8: Debug CORS Issues
```bash # Enable S3 server access logging aws s3api put-bucket-logging --bucket my-bucket \ --bucket-logging-status '{ "LoggingEnabled": { "TargetBucket": "log-bucket", "TargetPrefix": "s3-logs/" } }'
# Check logs for CORS errors aws s3 cp s3://log-bucket/s3-logs/ - | grep OPTIONS
# Use browser dev tools # 1. Open DevTools (F12) # 2. Go to Network tab # 3. Make request # 4. Check Response Headers for Access-Control-* headers ```
Step 9: Handle CORS with Presigned URLs
```bash # Presigned URLs work with CORS # Ensure the bucket CORS allows the origin making the request
# Generate presigned URL aws s3 presign s3://my-bucket/object.txt --expires-in 3600
# When using presigned URL from browser, CORS still applies # Make sure AllowedOrigins includes the page origin ```
Step 10: Clear Browser CORS Cache
Browsers cache CORS responses:
# CORS is cached for MaxAgeSeconds
# To test changes immediately:
# 1. Clear browser cache
# 2. Open in incognito/private window
# 3. Hard refresh (Ctrl+Shift+R)
# 4. Reduce MaxAgeSeconds temporarily for testingCORS Configuration Reference
| Field | Description | Example |
|---|---|---|
| AllowedOrigins | Origins that can access | ["https://example.com", "*"] |
| AllowedMethods | HTTP methods allowed | ["GET", "HEAD", "PUT", "POST"] |
| AllowedHeaders | Request headers allowed | ["Authorization", "Content-Type"] |
| ExposeHeaders | Response headers browser can see | ["ETag", "x-amz-request-id"] |
| MaxAgeSeconds | Preflight cache duration | 3000 |
Verification
```bash # Test from browser console fetch('https://my-bucket.s3.amazonaws.com/object.txt') .then(r => r.text()) .then(console.log) .catch(console.error)
# Should succeed without CORS error
# Test OPTIONS preflight curl -X OPTIONS \ -H "Origin: https://example.com" \ -H "Access-Control-Request-Method: PUT" \ -i https://my-bucket.s3.amazonaws.com/object.txt
# Should return 200 with Access-Control headers ```
Related Issues
- [Fix AWS S3 Bucket Policy Denied](/articles/fix-aws-s3-bucket-policy-denied)
- [Fix AWS S3 Replication Not Working](/articles/fix-aws-s3-replication-not-working)
- [Fix AWS Cloudflare CORS Errors](/articles/fix-cloudflare-redirect-loop)
Related Articles
- [AWS troubleshooting: Fix IAM Permission Denied - Complete Tro](fix-iam-permission-denied)
- [AWS cloud troubleshooting: AWS ACM Certificate Pending Validation Because the](aws-acm-certificate-pending-validation-wrong-route53-zone)
- [AWS cloud troubleshooting: AWS ALB Returns 502 Because the Target Closed the ](aws-alb-502-target-closed-connection-keepalive-timeout-mismatch)
- [AWS cloud troubleshooting: Fix AWS ALB CreateListener TargetGroupNotFound Err](aws-alb-createlistener-targetgroupnotfound)
- [AWS cloud troubleshooting: Fix Aws Alb Lambda 502 Bad Gateway Issue in AWS](aws-alb-lambda-502-bad-gateway)
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Fix AWS S3 CORS Configuration Missing", "description": "Fix S3 CORS errors. Configure bucket CORS rules, troubleshoot preflight requests, and enable cross-origin access.", "url": "https://www.fixwikihub.com/fix-aws-s3-cors-config-missing", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-04-01T21:33:28.035Z", "dateModified": "2026-04-01T21:33:28.035Z" } </script>