Asterrr's Handbook

Private access to S3 content

S3 presigned URLs, CloudFront signed URLs and signed cookies, origin access control, S3 Access Points, Multi-Region Access Points and Block Public Access.

Exam tasks: 2.3 (design security controls for data access, restrict access to content)

The decision: who is fetching the object (one user, many paying users, an application team, a global app), and must the request go through CloudFront, straight to S3, or both?

Choosing a mechanism

Side by side

S3 presigned URLCloudFront signed URLCloudFront signed cookies
Who signsAny IAM principal allowed to access the object, with its own credentialsYour app, with a private key whose public key is in a CloudFront trusted key groupSame as signed URLs
ScopeOne object and one operation (GET, PUT and so on)One URL, or a wildcard with a custom policyMany files, set once per session
PathStraight to S3Through CloudFront, with cachingThrough CloudFront, with caching
Upload supportYes, PUT or POSTYes, if the behavior allows itYes
RestrictionsExpiry only (plus bucket policy conditions)Expiry, start time and source IP range with a custom policySame as signed URLs
LifetimeUp to 7 days, and never longer than the signing credentialsYou chooseYou choose
URL changes neededNew URL per objectNew URL per objectNo. The URLs stay clean

Exam signal

"Let users upload directly to S3 without going through our servers" is a presigned URL (or presigned POST). "Paying subscribers stream many HLS segments" is signed cookies. "Download link for one purchased file, cached globally" is a CloudFront signed URL.

Presigned URLs that expire early

A presigned URL stops working when the credentials that signed it expire. If a Lambda function's role session signs a 7-day URL, the URL dies with the session, often within hours. Use longer-lived credentials only when you truly need long links, or switch to CloudFront signed URLs.

7 days
Maximum lifetime of a SigV4 presigned URL
Trusted key groups
The current way to register public keys for CloudFront signing, instead of root-account key pairs
On by default
Block Public Access and ACLs disabled for new buckets

Origin access control

Signed URLs restrict viewers. Origin access control (OAC) makes sure nobody skips CloudFront and reads the bucket directly.

  • CloudFront signs its requests to S3 with SigV4 as the cloudfront.amazonaws.com service principal.
  • The bucket policy allows s3:GetObject for that principal, with a condition on AWS:SourceArn equal to the distribution's ARN.
  • OAC supports SSE-KMS encrypted objects (add the distribution to the key policy), every Region, and PUT and DELETE requests.
  • OAC also protects Lambda function URL and MediaPackage origins.

Legacy: use Origin access control (OAC) instead

Origin access identity (OAI) was the earlier way to lock a bucket to CloudFront. It doesn't support SSE-KMS, doesn't work in Regions launched after December 2022, and can't sign dynamic requests such as PUT. Migrate existing OAIs to OAC.

OAC and S3 website endpoints

OAC and OAI work only with the S3 REST endpoint. A bucket configured as a static website endpoint is a custom origin, so you can't use OAC with it. To restrict it, the usual workaround is a secret custom header that the bucket policy checks. Better: use the REST endpoint with OAC and set a default root object in CloudFront.

S3 Access Points

An access point is a named network endpoint on a bucket with its own policy. Instead of one huge bucket policy, each application or team gets an access point.

  • Each access point can be restricted to a VPC, so requests must come through a VPC endpoint.
  • The bucket policy delegates access control to access points, for example by allowing any request that comes through an access point owned by the account.
  • Access points also work across accounts.

Exam signal

"Dozens of teams share a data lake bucket and the bucket policy is near its size limit" is solved with S3 Access Points, one per team, each with its own policy.

Multi-Region Access Points

  • One global endpoint in front of buckets in several Regions. Requests travel over the AWS global network to the closest bucket, using Global Accelerator.
  • Pair it with S3 Cross-Region Replication (which you configure) to keep the buckets in sync.
  • Failover controls switch between active/active and active/passive routing in minutes.
  • Requests are signed with SigV4A, which the AWS SDKs handle.

Block Public Access

  • Four settings (block new public ACLs, ignore existing public ACLs, block new public bucket policies, restrict access to buckets with public policies), set at the account level or per bucket, or organization-wide with an SCP that denies changing them.
  • On by default for new buckets, together with Object Ownership set to bucket owner enforced, which disables ACLs.
  • The account-level setting overrides any bucket or object that tries to be public.

Public bucket behind CloudFront

An answer that makes the bucket public "so CloudFront can read it" is always wrong. Keep Block Public Access on and use OAC.

S3 Object Lambda

Object Lambda runs a Lambda function on GET, HEAD or LIST requests to transform data on the way out, for example to redact PII or resize images. It moved to maintenance in November 2025 and is no longer available to new customers, so prefer a Lambda function behind API Gateway or CloudFront, or transform at write time.

Scenarios

Scenario · choose 2
A video training company sells courses of 200 short video segments each. Only subscribers may watch, the segments are cached globally, and the site's HTML links to each segment with its normal path. Nobody may download the segments straight from S3. Which TWO actions meet these requirements?
Scenario
A mobile app lets users upload profile videos of up to 500 MB. The company wants uploads to go straight to a private S3 bucket without passing through its API servers, and each upload link must work for only 15 minutes. What should the architect do?

Further reading

On this page