⚠️ Common Problem
If you’re uploading files to AWS S3 directly from the browser using a pre-signed URL (via JavaScript and a .NET Core API), you’ve probably faced this frustrating error:
Access to fetch at 'https://your-bucket.s3.amazonaws.com/...'
from origin 'http://localhost:3000' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
This happens because your S3 bucket does not allow requests from your frontend domain.
In this article, we’ll fix it step-by-step for both AWS S3 and your .NET Core API, with real working code and links to official AWS docs.
🧠 What is CORS?
CORS (Cross-Origin Resource Sharing) is a security feature of browsers that blocks JavaScript code from making requests to another domain unless explicitly allowed.
When your frontend (localhost:3000 or myapp.com) tries to upload to S3 (s3.amazonaws.com), the browser asks:
“Does this S3 bucket allow requests from my origin?”
If not, the request is blocked — even before reaching AWS.
🧱 Step 1. Configure CORS in Your AWS S3 Bucket
1️⃣ Go to your AWS S3 Console
2️⃣ Select your bucket → Permissions tab → Scroll to Cross-origin resource sharing (CORS)
3️⃣ Click Edit, and paste the following configuration:
[{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST"],
"AllowedOrigins": ["http://localhost:3000", "https://yourdomain.com"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000}]🧾 Explanation
| Key | Description |
|---|---|
AllowedOrigins | Domains allowed to access the bucket (add your frontend URLs) |
AllowedMethods | HTTP methods allowed (PUT for upload, GET for fetch) |
AllowedHeaders | Required when sending custom headers |
ExposeHeaders | Lets browser read specific headers (e.g., ETag) |
MaxAgeSeconds | Cache time for preflight results |
🔗 Official AWS Docs: Configuring CORS for S3
⚙️ Step 2. Generate Pre-Signed URL in .NET Core API
Create an endpoint in your .NET Core project to generate a pre-signed URL:
using Amazon.S3;
using Amazon.S3.Model;
using Microsoft.AspNetCore.Mvc;
using System;
namespace AwsUploadDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class S3UploadController : ControllerBase
{
private readonly IAmazonS3 _s3Client;
private const string BucketName = "your-bucket-name";
public S3UploadController(IAmazonS3 s3Client)
{
_s3Client = s3Client;
}
[HttpGet("generate-url")]
public IActionResult GeneratePresignedUrl(string fileName)
{
var key = $"uploads/{Guid.NewGuid()}_{fileName}";
var request = new GetPreSignedUrlRequest
{
BucketName = BucketName,
Key = key,
Verb = HttpVerb.PUT,
Expires = DateTime.UtcNow.AddMinutes(10),
ContentType = "image/jpeg"
};
var url = _s3Client.GetPreSignedURL(request);
return Ok(new
{
uploadUrl = url,
fileUrl = $"https://{BucketName}.s3.amazonaws.com/{key}"
});
}
}
}
👉 This API safely generates a short-lived pre-signed upload URL that your frontend can use.
🖥️ Step 3. Upload File from JavaScript (Frontend)
Here’s a minimal HTML + JavaScript example:

Join the conversation! Your thoughts help the community grow.