Quick Guide – Public Image Hosting on S3

This quick guide shows how to host images publicly on Amazon S3 and manage uploads with a least-privilege IAM user. Includes bucket policy, URL formats, and optional CORS.

Table of Contents

A fast, reliable way to serve images from S3, plus an IAM policy & user for managing uploads.

TL;DR
• Create an S3 bucket (optionally enable Static Website Hosting).
• Allow public read via a bucket policy.
• Create a least‑privilege IAM policy + user for uploads/deletes.
• Use the virtual‑hosted S3 URL to reference files in your site or app.


Prerequisites

  • AWS account with permissions to manage S3 and IAM
  • (Optional) AWS CLI installed and configured locally

1) Create the S3 bucket

  1. AWS Console → S3Create bucket
  2. Bucket name: e.g., acme-bucket-prod
  3. Region: us-east-1 (N. Virginia)
  4. Object Ownership: Bucket owner enforced (recommended) (disables ACLs)
  5. Block Public Access (bucket settings): Uncheck “Block all public access”, acknowledge the warning, then Create bucket.

Note: If your Account-level “Block Public Access” is On, it will override bucket settings—you’ll need to relax it at the account level for this bucket to be publicly readable.

Optional: Static Website Hosting

  • Open the bucket → PropertiesStatic website hostingEnable.
  • Set Index document (e.g., index.html) and Error document (e.g., 404.html).

2) Add a public‑read bucket policy

Open Bucket → Permissions → Bucket policy → Edit, and paste (replace BUCKET_NAME):

  • This grants public read of objects only. Writes remain private.
  • If account-level Public Access Block is enabled, the console will warn and this policy won’t take effect.

3) Create a least‑privilege IAM policy

Create a policy that limits a user to listing the bucket and putting/getting/deleting objects only in this bucket. Recommended for Object Ownership = bucket owner enforced (no ACLs):

If you must manage ACLs (generally not needed with Bucket owner enforced), add:
"s3:PutObjectAcl", "s3:GetObjectAcl" to the object‑level actions.

Create this in IAM → Policies → Create policy (JSON). Name it something like AcmeBucketManager.


4) Create an IAM user and access key

  1. IAM → Users → Create user (e.g., acme-image-uploader)
  2. Attach permissions: select your AcmeBucketManager policy
  3. Open the new user → Security credentialsCreate access keyApplication running outside AWSCreate
  4. Download the .csv or copy the Access key ID and Secret access key (you’ll only see the secret once)

Security tip: Prefer short‑lived credentials (roles + STS) in production. Use a long‑term access key only when necessary and rotate it regularly.


5) Upload & test (AWS CLI)


6) Public URL formats

Virtual‑hosted (recommended):

Example:

Path‑style:

Static website endpoint (if enabled; HTTP only on S3 endpoint):

Tips

  • URL‑encode spaces/special characters in OBJECT_KEY (e.g., space → %20).
  • Hyphens in bucket names are fine with HTTPS virtual‑hosted URLs.
  • If fronted by CloudFront, share the CloudFront URL instead.

7) Optional: CORS for browser apps

If a web app on another origin loads these images and you see CORS errors, add a CORS rule (Bucket → Permissions → CORS configuration):

Tighten AllowedOrigin to your site domains when known.


8) Production hardening (recommended)

  • Put CloudFront in front of S3 with Origin Access Control (OAC) so the bucket itself isn’t public.
  • Keep the bucket policy read‑only for anonymous users; never allow public writes.
  • Enable S3 server access logging and/or CloudFront logging; monitor for abuse.
  • Add lifecycle rules for object expiration if you publish ephemeral assets.
  • Consider AWS WAF on CloudFront to rate‑limit or block malicious traffic.

Have a project or a problem?

Talk with a senior engineer for practical recommendations—no obligation.

Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts

Categories

Get a free consultation from Reliable Penguin

Submit the form—or for immediate service call 866-649-7984.