AWS S3
file upload
public access
cloud storage
tutorial

How to upload files to AWS S3 with public access granted?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Uploading a file to Amazon S3 and making it publicly readable is no longer just a matter of adding public-read everywhere. Modern S3 defaults are intentionally restrictive: Block Public Access may be enabled, and object ACLs may be disabled entirely. The safest approach is to decide first whether the object truly needs to be public, then publish it with a bucket policy or a CDN-aware design.

Understand the Current S3 Defaults

S3 now tries hard to prevent accidental public exposure. Two settings matter immediately:

  • Block Public Access can prevent public bucket policies or public ACLs
  • Object Ownership can disable ACL-based access entirely

That means an old command such as this may fail or do nothing useful in a modern bucket:

bash
aws s3 cp photo.jpg s3://my-public-bucket/photo.jpg --acl public-read

If ACLs are disabled, --acl public-read is the wrong tool. In many setups, the better pattern is private upload plus bucket policy for public reads.

If the bucket is intended to host public assets, upload normally and allow public GetObject through a bucket policy.

Upload the object:

bash
aws s3 cp photo.jpg s3://my-public-bucket/photo.jpg

Then attach a bucket policy like this:

json
1{
2  "Version": "2012-10-17",
3  "Statement": [
4    {
5      "Sid": "AllowPublicReadForObjects",
6      "Effect": "Allow",
7      "Principal": "*",
8      "Action": "s3:GetObject",
9      "Resource": "arn:aws:s3:::my-public-bucket/*"
10    }
11  ]
12}

This grants public read access to objects in that bucket without requiring public ACLs on each upload.

If Public Access Is Blocked

If the bucket or account still has Block Public Access settings that deny public policies, you must change that explicitly before the policy can work.

Do that only for buckets meant to contain public content such as images, static assets, or downloadable documents. Do not disable those protections for general-purpose private storage.

Uploading with the AWS SDK

The same principle applies in code. Here is a simple example with the AWS SDK for JavaScript v3:

javascript
1import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
2import { readFile } from "node:fs/promises";
3
4const client = new S3Client({ region: "us-east-1" });
5const body = await readFile("./photo.jpg");
6
7await client.send(
8  new PutObjectCommand({
9    Bucket: "my-public-bucket",
10    Key: "photo.jpg",
11    Body: body,
12    ContentType: "image/jpeg"
13  })
14);
15
16console.log("Uploaded");

If the bucket policy already grants public reads, the uploaded object becomes publicly readable at the normal S3 object URL.

Consider a Better Public-Asset Pattern

For production applications, direct public S3 access is not always the best choice. Many teams prefer CloudFront in front of S3 because it gives better caching, TLS handling, logging, and origin protection.

So ask whether you really want:

  • a publicly readable S3 object
  • a private S3 bucket behind CloudFront
  • a private object with a presigned URL instead of open public access

Those are different security models.

Common Pitfalls

A common mistake is assuming --acl public-read is the modern default answer. In many current S3 buckets, ACLs are disabled, so that flag is either blocked or irrelevant.

Another mistake is disabling Block Public Access on a bucket that also stores private files. Public and private content should usually be separated into different buckets or different delivery patterns.

A third issue is forgetting ContentType. The upload succeeds, but the browser may not serve the file the way you expect.

Finally, if the object is encrypted with KMS or fronted by a different access pattern, public reads may still fail for reasons unrelated to the upload command itself.

Summary

  • Modern S3 buckets often block public ACLs or disable ACLs entirely
  • The usual current pattern is normal upload plus a public-read bucket policy
  • Change Block Public Access only when the bucket is intentionally public
  • SDK uploads do not need special public flags if bucket policy already grants reads
  • For production delivery, consider CloudFront or presigned URLs instead of raw public S3 access

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.