Jaconir

AWS S3 CORS Error: How to Fix It and Generate the Config

technical
Developer
March 29, 2026
6 min read

If you are a web developer loading assets, fonts, or performing API requests directly against an Amazon S3 bucket from your frontend application, you have almost certainly encountered this demoralizing red error message in your developer console:

Access to fetch at 'https://my-bucket.s3.amazonaws.com/image.jpg' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

This means your web browser has preemptively blocked the underlying request because the target S3 bucket does not explicitly permit your domain to access it.

The fix is straightforward: you need to attach a CORS (Cross-Origin Resource Sharing) configuration to your bucket.

What is CORS and Why Do S3 Buckets Block It?

CORS is a browser security mechanism designed to prevent malicious websites from making unauthorized requests to another domain on your behalf. By default, S3 buckets strictly adhere to the Same-Origin Policy, meaning they will reject any requests emanating from a domain name that doesn't exactly match the bucket's intrinsic AWS subdomain.

To bypass this restriction, you must inform AWS S3 which external domains (origins) you trust, alongside what HTTP methods (GET, PUT, POST, DELETE) they are allowed to use.

How to Fix the AWS S3 CORS Error

Historically, AWS used an XML format for defining CORS rules, but this was updated to a cleaner JSON format. If you are looking at old stack overflow posts showing XML, ignore them.

Follow these steps to add your CORS configuration:

1. Via the AWS Console

  1. Log in to your AWS Management Console and navigate to S3.
  2. Click on the name of the bucket causing the CORS issue.
  3. Select the Permissions tab.
  4. Scroll down to the Cross-origin resource sharing (CORS) section.
  5. Click Edit.
  6. Paste your JSON CORS configuration into the text box and click Save changes.

2. The JSON CORS Configuration Example

Here is a robust starting configuration that permits standard cross-origin reads (GET, HEAD requests) from any domain:

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "HEAD"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [],
        "MaxAgeSeconds": 3000
    }
]

Working with Messy JSON Configs?

Ensure your S3 CORS configuration is syntactically correct and properly formatted before applying it.

Format JSON Configuration

Understanding the JSON CORS Fields

A CORS configuration is structured as an array, letting you define up to 100 distinct rules. Let's examine what each parameter achieves:

AllowedOrigins

The most critical field. It defines the domains permitted to access your S3 bucket.

  • ["*"]: Allows absolutely any domain. This is generally safe if your bucket only contains public, read-only assets like images or static scripts.
  • ["http://localhost:3000", "https://www.jaconir.com"]: Locks down access to specific development and production domains. Note that https://jaconir.com and https://www.jaconir.com are technically different origins.

AllowedMethods

The HTTP verbs the browser is allowed to execute.

  • For reading public assets (fonts, images, JSON files), you only want ["GET", "HEAD"].
  • If your frontend allows users to upload files directly to S3 via pre-signed URLs, you must include ["PUT", "POST"].

AllowedHeaders

Specifies which custom HTTP headers the frontend is allowed to include in the request. During preflight (OPTIONS) requests, the browser asks S3 if it supports the requested headers. To avoid frustrating debug sessions, ["*"] is a practical default unless your compliance requires strict header logging.

ExposeHeaders

Allows the browser's JavaScript environment to actually read the response headers sent back by S3. Common values include ETag (for caching) and x-amz-server-side-encryption.

MaxAgeSeconds

The duration (in seconds) that the browser is allowed to cache the CORS permission preflight response.

  • Setting this to 3000 (50 minutes) reduces the overhead of repetitive OPTIONS requests during high-traffic sessions.

Troubleshooting Lingering Issues

If you've applied the JSON configuration and the dreaded red console error persists, verify the following:

1. Browser Cache Browsers aggressively cache CORS preflight failures. Perform a hard refresh (Ctrl + Shift + R or Cmd + Shift + R), or test loading your frontend in Incognito mode.

2. Trailing Slashes in Origins Your allowed origins must not include trailing slashes.

  • ["https://www.mywebsite.com/"]
  • ["https://www.mywebsite.com"]

3. CloudFront Precedence If you are serving your S3 bucket through AWS CloudFront, ensure your CloudFront distribution is configured to forward Origin and Access-Control-Request-* headers. Otherwise, CloudFront strips the origin information before it ever hits S3, making your CORS rules useless.

Conclusion

The S3 CORS block is a frustrating rite of passage for all developers utilizing AWS asset storage. By writing a small snippet of JSON explicitly defining your trusted domains and HTTP methods, you can clear the browser restrictions in minutes and resume building your application.