← network.liucao.me

S3 + CloudFront CORS 配置

什么时候需要配 CORS、怎么配、配在哪里

什么时候需要配 CORS

用户点链接下载文件 → 不需要 CORS。只有前端 JavaScript 用 fetch/XHR 跨域读取文件时才需要。
场景需要 CORS?
用户点 <a href> 下载不需要
window.open(presignedUrl)不需要
<img> / <video> 加载不需要
后端/服务器请求 S3不需要
手机原生 APP 请求不需要
前端 JS fetch() 跨域读文件需要
前端 JS 下载文件显示进度条需要
Canvas toBlob() 跨域图片需要
Web 字体从 CDN 加载需要

CORS 30s

your-app.com 前端 JS: fetch() 浏览器 自动加 Origin 头 CloudFront / S3 返回 CORS 响应头 Origin: your-app.com Access-Control-Allow-Origin: * 有 CORS 头 → JS 拿到数据 没有 → 浏览器拦截响应 请求一定会到达服务器。浏览器拦截的是「响应给 JS」这一步,不是请求本身。 服务器(S3/CF)通过响应头决定谁可以读。前端无法绕过。

配置方案

方案 1: CF Response Headers Policy方案 2: S3 Bucket CORS
CORS 头来源CloudFront 强制插入S3 返回
需要改 S3?不需要需要
需要转发 Origin?不需要需要(ORP 配 Origin header)
复杂度
适用通用,推荐需要精细控制哪些 origin

方案 1:CloudFront Response Headers Policy 推荐

在 CloudFront 层强制给所有响应加 CORS 头,无需修改 S3 桶配置。

Step 1: 创建 Response Headers Policy

aws cloudfront create-response-headers-policy \
  --response-headers-policy-config '{
    "Name": "CORS-AllowAll",
    "Comment": "Allow all origins for GET/HEAD",
    "CorsConfig": {
      "AccessControlAllowOrigins": {"Quantity": 1, "Items": ["*"]},
      "AccessControlAllowMethods": {"Quantity": 2, "Items": ["GET", "HEAD"]},
      "AccessControlAllowHeaders": {"Quantity": 1, "Items": ["*"]},
      "AccessControlExposeHeaders": {"Quantity": 0, "Items": []},
      "AccessControlAllowCredentials": false,
      "AccessControlMaxAgeSec": 86400,
      "OriginOverride": true
    }
  }'

Step 2: 绑定到 Distribution Behavior

Console → Distribution → Behavior → Edit → Response headers policy → 选择 CORS-AllowAll

Step 3: 验证

curl -I -X OPTIONS \
  -H "Origin: https://your-app.com" \
  -H "Access-Control-Request-Method: GET" \
  https://d1234.cloudfront.net/file.jpg

# access-control-allow-origin: *
# access-control-allow-methods: GET, HEAD

方案 2:S3 Bucket CORS 配置

让 S3 自己返回 CORS 头。需要 CloudFront 转发 Origin header。

Step 1: S3 CORS

aws s3api put-bucket-cors --bucket your-bucket --cors-configuration '{
  "CORSRules": [{
    "AllowedOrigins": ["https://your-app.com"],
    "AllowedMethods": ["GET", "HEAD"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["Content-Length", "Content-Type"],
    "MaxAgeSeconds": 3600
  }]
}'

Step 2: ORP 转发 Origin

# managed ORP: CORS-S3Origin (88a5eaf4-2fd4-4709-b370-b4c650ea3fcf)
# 或自定义 ORP:
aws cloudfront create-origin-request-policy \
  --origin-request-policy-config '{
    "Name": "ForwardOriginAndQS",
    "HeadersConfig": {"HeaderBehavior": "whitelist", "Headers": {"Quantity": 1, "Items": ["Origin"]}},
    "CookiesConfig": {"CookieBehavior": "none"},
    "QueryStringsConfig": {"QueryStringBehavior": "all"}
  }'
注意:ORP 不转发 Origin → S3 不返回 CORS 头 → 前端报跨域。最常见的坑。

Step 3: Cache Policy 加 Origin

缓存开启时必须把 Origin 加入 Cache Key,否则无 CORS 头的缓存会影响后续请求。

常见问题

Q: 配了 S3 CORS 但还是报跨域?
A: CloudFront 没有转发 Origin header。检查 ORP。
Q: 第一次正常,后续报跨域?
A: Cache Key 没包含 Origin。用方案 1 或 Cache Policy 加 Origin。
Q: 设 * 有安全风险吗?
A: 对公开文件(presigned URL / CDN)没有。CORS 头只决定 JS 能否读响应,不影响文件本身的访问控制。

S3 + CloudFront CORS Setup

When you need CORS, how to configure it, and where

When Do You Need CORS

User clicks a link to download → No CORS needed. CORS only applies when frontend JavaScript uses fetch/XHR to read cross-origin responses.
ScenarioCORS needed?
User clicks <a href> to downloadNo
window.open(presignedUrl)No
<img> / <video> tag loadingNo
Backend / server-side requestsNo
Native mobile APPNo
Frontend JS fetch() cross-originYes
JS download with progress barYes
Canvas toBlob() on cross-origin imageYes
Web fonts from CDNYes

CORS 30s

your-app.com JS: fetch() Browser Adds Origin header CloudFront / S3 Returns CORS headers Origin: your-app.com Access-Control-Allow-Origin: * CORS header present → JS gets data Missing → Browser blocks response The request always reaches the server. The browser blocks the JS from reading the response, not the request itself. The server decides who can read via response headers. Frontend cannot bypass this.

Configuration Options

Option 1: CF Response Headers PolicyOption 2: S3 Bucket CORS
CORS header sourceCloudFront injectsS3 returns
Modify S3?NoYes
Forward Origin header?NoYes (via ORP)
ComplexityLowMedium
Best forGeneral use, recommendedFine-grained origin control

Option 1: CloudFront Response Headers Policy Recommended

CloudFront injects CORS headers into every response. No S3 changes required.

Step 1: Create Response Headers Policy

aws cloudfront create-response-headers-policy \
  --response-headers-policy-config '{
    "Name": "CORS-AllowAll",
    "Comment": "Allow all origins for GET/HEAD",
    "CorsConfig": {
      "AccessControlAllowOrigins": {"Quantity": 1, "Items": ["*"]},
      "AccessControlAllowMethods": {"Quantity": 2, "Items": ["GET", "HEAD"]},
      "AccessControlAllowHeaders": {"Quantity": 1, "Items": ["*"]},
      "AccessControlExposeHeaders": {"Quantity": 0, "Items": []},
      "AccessControlAllowCredentials": false,
      "AccessControlMaxAgeSec": 86400,
      "OriginOverride": true
    }
  }'

Step 2: Attach to Distribution Behavior

Console → Distribution → Behavior → Edit → Response headers policy → Select CORS-AllowAll

Step 3: Verify

curl -I -X OPTIONS \
  -H "Origin: https://your-app.com" \
  -H "Access-Control-Request-Method: GET" \
  https://d1234.cloudfront.net/file.jpg

# access-control-allow-origin: *
# access-control-allow-methods: GET, HEAD

Option 2: S3 Bucket CORS

S3 returns CORS headers natively. Requires CloudFront to forward the Origin header.

Step 1: Configure S3 CORS

aws s3api put-bucket-cors --bucket your-bucket --cors-configuration '{
  "CORSRules": [{
    "AllowedOrigins": ["https://your-app.com"],
    "AllowedMethods": ["GET", "HEAD"],
    "AllowedHeaders": ["*"],
    "ExposeHeaders": ["Content-Length", "Content-Type"],
    "MaxAgeSeconds": 3600
  }]
}'

Step 2: ORP must forward Origin

# Managed ORP: CORS-S3Origin (88a5eaf4-2fd4-4709-b370-b4c650ea3fcf)
# Or custom ORP:
aws cloudfront create-origin-request-policy \
  --origin-request-policy-config '{
    "Name": "ForwardOriginAndQS",
    "HeadersConfig": {"HeaderBehavior": "whitelist", "Headers": {"Quantity": 1, "Items": ["Origin"]}},
    "CookiesConfig": {"CookieBehavior": "none"},
    "QueryStringsConfig": {"QueryStringBehavior": "all"}
  }'
Warning: If ORP doesn't forward Origin → S3 won't return CORS headers → frontend gets CORS error. This is the #1 pitfall.

Step 3: Include Origin in Cache Key

With caching enabled, Origin must be in the Cache Key. Otherwise a cached response without CORS headers will be served to subsequent requests that include Origin.

FAQ

Q: Configured S3 CORS but still getting CORS errors?
A: CloudFront isn't forwarding the Origin header to S3. Check your ORP.
Q: First request works, subsequent ones fail?
A: Cache Key doesn't include Origin. Use Option 1, or add Origin to your Cache Policy.
Q: Is setting * a security risk?
A: For public files (presigned URLs / CDN delivery), no. CORS headers only control whether JS can read the response — the file itself is already accessible via URL. CORS doesn't increase the attack surface.