☁️
Amazon Lightsail

Static Sites — Phase 1

Private bucket origins via OAC, default root objects, and custom error responses for Lightsail CDN distributions.

CLI Ready Console Coming Soon FAC Gated

New Features

Private Origin Access (OAC)

Serve content from a private S3 bucket through CloudFront using Origin Access Control. The bucket is only accessible via the CDN — direct S3 access is denied.

Adds OAC + bucket policy automatically
Cache invalidation on disable
Async retry via follow-up job
enablePrivateOriginAccess: true

Default Root Object

Specify which file CloudFront returns when a viewer requests the root URL (/). Essential for static sites using S3 bucket origins that have no built-in index handling.

Edge-level rewrite (root only)
Max 512 chars, path-safe characters
Not needed for instance origins
defaultRootObject: "index.html"

Custom Error Responses

Map HTTP error codes to custom response pages with configurable caching TTL. Enables SPA routing (404 → 200 + /index.html) and branded error pages.

Up to 10 error response rules
11 CloudFront error codes supported
Configurable TTL (0 – 1 year)
errorCode: 404 → responseCode: "200"

APIs Affected

All new fields are gated behind the static_sites_phase_1 FAC flag. When the flag is off, fields are silently nulled.

CreateDistribution POST
New Request Fields
enablePrivateOriginAccess
defaultRootObject
customErrorResponses[]
New Response Fields
origin.isPrivateOriginAccessEnabled
defaultRootObject
customErrorResponses[]
UpdateDistribution PUT
New Request Fields
enablePrivateOriginAccess
defaultRootObject
customErrorResponses[]
Key Behaviors

null = don't touch (PATCH semantics)

"" for DRO = clear it

[] for CER = clear all

• Origin required with private access

GetDistributions GET
New Response Fields
origin.isPrivateOriginAccessEnabled
defaultRootObject
customErrorResponses[]
Notes

• DRO + CER only on single-name calls

• isPrivateOriginAccessEnabled: Bucket only (null for others)

• Fields absent when FAC off

How Private Origin Access Works

👤
Customer
Calls UpdateDistribution
Lightsail Service
Creates OAC + Bucket Policy
🌐
CloudFront + S3
SigV4 signed origin fetch
✅ Enable (enablePrivateOriginAccess = true)
  1. Find or create OAC in shadow account
  2. Attach OAC to CloudFront distribution config
  3. Add bucket policy: CloudFront service principal + aws:SourceArn condition
  4. Persist isPrivateOriginAccessEnabled = true
  5. If policy write fails → schedule retry via follow-up job
❌ Disable (enablePrivateOriginAccess = false)
  1. Remove bucket policy statement (SID-based lookup)
  2. Invalidate edge cache (/* wildcard) — content stops in ~2 min
  3. Persist isPrivateOriginAccessEnabled = false
  4. Best-effort: invalidation failure logged, doesn't block

Example: SPA Routing with Custom Error Responses

Configure your distribution to serve your React/Vue/Angular app's index.html on 404, enabling client-side routing.

CLI Command
aws lightsail update-distribution \
  --distribution-name my-spa \
  --default-root-object index.html \
  --custom-error-responses '[
    {
      "errorCode": 404,
      "responseCode": "200",
      "responsePagePath": "/index.html",
      "errorCachingMinTTL": 10
    },
    {
      "errorCode": 403,
      "responseCode": "200",
      "responsePagePath": "/index.html",
      "errorCachingMinTTL": 10
    }
  ]'
Result
GET / Serves index.html (DRO)
GET /about S3 returns 404 → CF serves /index.html with 200
GET /app/dashboard Same — client router handles /app/dashboard
The SPA's JavaScript router picks up the path and renders the correct view. No server-side routing needed.

Input Validation

Field Constraint Error
defaultRootObject≤ 512 chars, no leading /, no .., alphanumeric + ._/-InvalidInput
customErrorResponsesMax 10 entries, unique errorCodesInvalidInput
errorCodeOne of: 400, 403, 404, 405, 414, 416, 500, 501, 502, 503, 504InvalidInput
responseCodeNumeric, 200–599InvalidInput
responsePagePathMust start with /InvalidInput
errorCachingMinTTL0 – 31,536,000 secondsInvalidInput
responseCode + responsePagePathMust both be present or both omittedInvalidInput
enablePrivateOriginAccessRequires origin (name + region) on Update; Bucket origins only for enableInvalidInput