AccessControlAllowOrigin null

AWS S3 CORS PUT Fails: AccessControlAllowOrigin null

Browser PUT to S3 fails with 'AccessControlAllowOrigin null' because CORS config misses the PUT method or allowed origin. Fix the CORS rules and re-test.

You're building a web app that uploads files straight from the browser to S3 using the AWS SDK. You've set up a CORS policy on the bucket, but every PUT request dies with AccessControlAllowOrigin null. The browser console shows a CORS error, the network tab shows the preflight OPTIONS request getting a 403, and your upload never starts. This happens almost always when the CORS configuration is incomplete or slightly off — usually missing the PUT method or the allowed origin doesn't match your site's domain.

Why does this happen?

Before a browser sends a PUT request to a different domain, it fires an OPTIONS preflight. The server (S3) needs to respond with Access-Control-Allow-Origin and Access-Control-Allow-Methods. If your CORS rules don't explicitly allow PUT, or the origin in the rule doesn't match the request's Origin header, S3 returns a response with no Access-Control-Allow-Origin header — that's what the SDK reports as AccessControlAllowOrigin null.

Another common culprit is using a wildcard * for AllowedOrigin but also setting AllowedHeaders incorrectly. S3 can't return a wildcard origin when credentials are involved, and some SDKs automatically include them.

The fix — update your CORS policy

Here's the exact step-by-step that has fixed this for me dozens of times.

  1. Open the S3 bucket in the AWS Console.
  2. Go to Permissions tab → Cross-origin resource sharing (CORS).
  3. Click Edit and replace the existing policy with the following, customizing the AllowedOrigin to your actual domain:
[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["PUT", "POST", "GET"],
    "AllowedOrigins": ["https://your-app.com"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3000
  }
]

If you're testing from a localhost, add http://localhost:3000 (or whatever port) to the AllowedOrigins array. Don't use * unless you're absolutely sure you're not sending credentials.

  1. Save the policy. S3 propagates CORS changes in a few seconds, but give it a minute.
  2. Hard-refresh your browser (Ctrl+Shift+R) to clear cached preflight responses.

Still failing? Check these three things

If the error persists, the problem is usually something outside the CORS policy itself.

1. Your bucket policy is blocking the preflight

If your bucket policy denies OPTIONS requests, CORS rules won't matter — S3 rejects the preflight before it even checks CORS. Make sure your bucket policy allows the s3:PutObject and s3:GetObject actions for the specific IAM role or user you're using. A common mistake is allowing only the final PUT but not the OPTIONS request. S3 treats OPTIONS as a separate permission check.

2. The SDK is sending a header you didn't allow

Some SDKs (like AWS SDK v3) add content-type or custom headers during upload. If your AllowedHeaders is set to a specific list instead of *, the preflight will fail if it doesn't match. Check the network tab for the OPTIONS request, look at the Access-Control-Request-Headers header, and make sure those names are in your AllowedHeaders list.

3. You're mixing the SDK version

Using the old v2 SDK with a v3-only feature? That can cause unexpected behavior. For browser uploads, I'd recommend using the @aws-sdk/client-s3 package and the PutObjectCommand. If you're using the older AWS.S3 from aws-sdk, make sure your region is set correctly. A mismatched region often leads to a redirect, and S3's redirect response doesn't include the CORS headers — exactly what causes the null error.

Verify your fix

After updating, run a test from the browser console using the AWS SDK:

const upload = new S3Client({
  region: 'us-east-1',
  credentials: {
    accessKeyId: 'YOUR_KEY',
    secretAccessKey: 'YOUR_SECRET'
  }
});

await upload.send(new PutObjectCommand({
  Bucket: 'your-bucket',
  Key: 'test.txt',
  Body: 'hello'
}));

If the upload succeeds, you're done. If it still throws AccessControlAllowOrigin null, open the browser dev tools, click on the OPTIONS request in the Network tab, and inspect the response headers. You'll likely see the reason — either the Origin doesn't match, or the method isn't allowed. Fix that specific mismatch and you're golden.

One last thing: don't waste time with browser extensions or disabling CORS. That's not the real issue. The root cause is always the server-side CORS configuration or a preflight request blocked by the bucket policy. Get those right, and your uploads will work.

Related Errors in Server & Cloud
0X0000170D Cluster config already committed error 0X0000170D fix DEADLINE_EXCEEDED or 504 Gateway Timeout Cloud Function Timing Out on Cold Start? Fix the Warmup Request 0X80004026 CO_E_SERVER_NOT_PAUSED (0x80004026) Quick Fix Serverless Function Times Out After 30 Seconds? Here’s the Fix

Was this solution helpful?

EP
Erropedia Team
Tech Support Editors
The Erropedia editorial team researches and documents real-world tech errors from across Windows, Linux, macOS, networking, databases, cloud platforms, and more. Every solution is reviewed for accuracy and updated as software and systems evolve.