為什麼使用 CloudFront 訪問 S3 私有內容?
核心價值與三大優勢
在 AWS 雲端架構中,Amazon S3 與 Amazon CloudFront 的結合是內容分發的黃金組合。特別是在處理私有內容(如付費媒體、內部文件、受保護的資源)時,這種架構能同時滿足「安全性」、「效能」、「成本」三大需求。
1. 全球加速與低延遲
CloudFront 擁有遍佈全球的邊緣節點(Edge Locations),當使用者請求內容時:
- 就近存取:自動路由到最近的邊緣節點
- 快取機制:首次請求後內容被快取在邊緣節點,後續請求直接從快取提供
- 延遲降低:相較於直接訪問 S3,延遲可降低 50-80%
- 頻寬優化:減少回源(Origin)請求,降低 S3 出站流量成本
實際案例:若 S3 存儲桶位於美國東部(us-east-1),亞洲使用者直接訪問延遲約 200-300ms;透過 CloudFront 東京節點,延遲可降至 20-50ms。
2. 強化安全性與存取控制
直接開放 S3 存儲桶為公開訪問存在重大安全風險。透過 CloudFront + OAI/OAC 方案:
- S3 保持私有:存儲桶完全不對外開放,僅允許 CloudFront 訪問
- 單一入口:所有請求必須經過 CloudFront,無法繞過直接訪問 S3
- 簽名 URL:可配合 CloudFront Signed URLs 實現時效性存取控制
- WAF 整合:CloudFront 可整合 AWS WAF,阻擋惡意請求、DDoS 攻擊
- 地理限制:可設定特定國家/地區的存取限制
3. 成本優化與流量管理
- 降低 S3 請求費用:CloudFront 快取減少對 S3 的 GET 請求次數
- 出站流量成本:CloudFront 出站流量費率通常低於 S3 直接傳輸
- 免費方案:AWS Free Tier 每月提供 1TB CloudFront 出站流量與 1000 萬次請求
- 可預測費用:透過 TTL 設定控制快取時間,平衡成本與即時性
Origin Access Identity (OAI) vs Origin Access Control (OAC)
技術演進與選擇建議
AWS 提供兩種方式讓 CloudFront 安全訪問 S3:OAI(舊方案)與 OAC(新方案)。
Origin Access Identity (OAI) – 傳統方案
工作原理:
- 建立一個特殊的 CloudFront 身份(OAI)
- 在 S3 Bucket Policy 中授權該 OAI 讀取權限
- CloudFront 使用 OAI 身份向 S3 請求內容
限制:
- ❌ 不支援 S3 伺服器端加密(SSE-KMS)
- ❌ 不支援動態請求(POST, PUT, DELETE)
- ❌ AWS 官方已標註為 Legacy(傳統功能)
Origin Access Control (OAC) – 新一代方案(推薦)
增強功能:
- ✅ 完整支援 S3 SSE-KMS 加密
- ✅ 支援所有 HTTP 方法(GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
- ✅ 更強的安全性(使用 AWS Signature Version 4)
- ✅ 更細緻的權限控制
建議:新專案請直接使用 OAC;舊專案建議逐步遷移至 OAC。
完整配置步驟(使用 OAC)
步驟 1:建立 S3 存儲桶
# 使用 AWS CLI 建立存儲桶
aws s3 mb s3://my-private-content-bucket --region us-east-1
# 確保阻擋所有公開存取
aws s3api put-public-access-block
--bucket my-private-content-bucket
--public-access-block-configuration
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
步驟 2:上傳測試內容
# 上傳測試檔案
echo "This is a private file" > test.txt
aws s3 cp test.txt s3://my-private-content-bucket/test.txt
# 確認無法直接訪問(應返回 403)
curl https://my-private-content-bucket.s3.amazonaws.com/test.txt
步驟 3:建立 CloudFront Distribution 與 OAC
進入 CloudFront Console → Create Distribution:
- Origin Settings:
- Origin Domain:選擇你的 S3 存儲桶(my-private-content-bucket.s3.us-east-1.amazonaws.com)
- Origin Access:選擇 Origin Access Control Settings (recommended)
- 點擊 Create New OAC,使用預設設定
- Default Cache Behavior:
- Viewer Protocol Policy:Redirect HTTP to HTTPS(強制 HTTPS)
- Allowed HTTP Methods:選擇 GET, HEAD(唯讀)或 GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE(讀寫)
- Cache Policy:CachingOptimized(推薦)
- Settings:
- Price Class:選擇地理範圍(Use All Edge Locations / Use Only North America and Europe)
- Alternate Domain Names (CNAMEs):若有自訂網域,在此輸入
- Custom SSL Certificate:若使用自訂網域,需上傳 SSL 憑證
- 點擊 Create Distribution
步驟 4:複製並套用 S3 Bucket Policy
CloudFront 建立完成後,會顯示一個 橘色提示框,點擊 Copy Policy 按鈕,會得到類似以下的 JSON 策略:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-private-content-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E1234EXAMPLE"
}
}
}
]
}
前往 S3 Console → 選擇存儲桶 → Permissions → Bucket Policy → 貼上策略 → Save。
步驟 5:驗證訪問
# 取得 CloudFront Domain Name(例如 d123abc456def.cloudfront.net)
CLOUDFRONT_DOMAIN="d123abc456def.cloudfront.net"
# 透過 CloudFront 訪問(應成功)
curl https://${CLOUDFRONT_DOMAIN}/test.txt
# 直接訪問 S3(應失敗,返回 403)
curl https://my-private-content-bucket.s3.amazonaws.com/test.txt
進階配置:CloudFront Signed URLs
實現時效性存取控制
若需要更細緻的權限控制(如付費內容、限時下載連結),可使用 CloudFront Signed URLs。
配置步驟
1. 建立 CloudFront Key Pair
- 登入 AWS 根帳戶(Root Account)
- 前往 Security Credentials → CloudFront Key Pairs → Create New Key Pair
- 下載私鑰(pk-XXXXX.pem)並記錄 Access Key ID
2. 設定 CloudFront Distribution 為 Restricted
- 編輯 Distribution → Behaviors → 選擇 Default Behavior
- Restrict Viewer Access:選擇 Yes
- Trusted Signers:選擇 Self(使用自己的 AWS 帳戶)
3. 使用 Python 生成 Signed URL
from datetime import datetime, timedelta
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend
import base64
import json
def create_signed_url(url, key_pair_id, private_key_path, expiration_minutes=60):
# 設定過期時間
expiration = datetime.utcnow() + timedelta(minutes=expiration_minutes)
expiration_timestamp = int(expiration.timestamp())
# 建立策略
policy = {
"Statement": [{
"Resource": url,
"Condition": {
"DateLessThan": {
"AWS:EpochTime": expiration_timestamp
}
}
}]
}
policy_json = json.dumps(policy, separators=(',', ':'))
policy_b64 = base64.b64encode(policy_json.encode()).decode().replace('+', '-').replace('=', '_').replace('/', '~')
# 載入私鑰
with open(private_key_path, 'rb') as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
backend=default_backend()
)
# 簽名
signature = private_key.sign(policy_json.encode(), padding.PKCS1v15(), hashes.SHA1())
signature_b64 = base64.b64encode(signature).decode().replace('+', '-').replace('=', '_').replace('/', '~')
# 組合 Signed URL
signed_url = f"{url}?Policy={policy_b64}&Signature={signature_b64}&Key-Pair-Id={key_pair_id}"
return signed_url
# 使用範例
url = "https://d123abc456def.cloudfront.net/premium/video.mp4"
key_pair_id = "APKAXXXXXXXXXXXXXXXX"
private_key_path = "/path/to/pk-XXXXX.pem"
signed_url = create_signed_url(url, key_pair_id, private_key_path, expiration_minutes=30)
print(f"Signed URL (30分鐘有效): {signed_url}")
常見問題 FAQ
Q1: CloudFront 訪問 S3 返回 403 Forbidden,如何排查?
可能原因與解決方案:
- Bucket Policy 未正確設定
- 確認
Resource是否包含/*(例如arn:aws:s3:::bucket-name/*) - 確認
AWS:SourceArn的 Distribution ID 是否正確
- 確認
- S3 區塊公開存取設定
- 確認沒有阻擋 CloudFront 的訪問(應只阻擋公開存取,不影響 OAC)
- CloudFront Distribution 尚未部署完成
- 檢查 Distribution Status 是否為 Deployed(初次建立需 15-20 分鐘)
- 快取問題
- 建立 Invalidation 清除快取:
aws cloudfront create-invalidation --distribution-id E1234 --paths "/*"
- 建立 Invalidation 清除快取:
Q2: 如何確認請求是否真的來自 CloudFront?
驗證方法:
- 檢查 S3 Access Logs
- 啟用 S3 Access Logging
- 檢查 Requester ARN 是否包含
cloudfront.amazonaws.com
- CloudFront Access Logs
- 啟用 CloudFront Logging,查看
x-edge-location,x-edge-result-type欄位
- 啟用 CloudFront Logging,查看
- 使用自訂 Header 驗證
- 在 CloudFront Behavior 中加入自訂 Header(例如
X-Custom-Secret: abc123) - 在應用層檢查此 Header,只接受帶有正確 Header 的請求
- 在 CloudFront Behavior 中加入自訂 Header(例如
Q3: OAI 與 OAC 可以共存嗎?如何遷移?
答案:可以共存,但建議逐步遷移
遷移步驟:
- 在現有 Distribution 中建立新的 Origin,使用 OAC
- 修改 Behavior,將部分路徑(如
/new/*)指向新 Origin - 逐步測試,確認功能正常
- 更新 S3 Bucket Policy,同時授權 OAI 與 OAC(過渡期)
- 確認所有流量已切換至 OAC 後,移除 OAI 相關設定
Q4: 如何設定不同檔案類型的快取時間?
使用 Cache Behaviors 與 TTL 設定:
- 前往 CloudFront Distribution → Behaviors → Create Behavior
- 設定 Path Pattern(例如
*.jpg,*.png,*.css) - 設定 TTL(Time To Live):
- 靜態資源(圖片、CSS、JS):Minimum TTL = 86400(1天),Maximum TTL = 31536000(1年)
- 動態內容(API 回應):Minimum TTL = 0,Maximum TTL = 3600(1小時)
範例:針對圖片設定長快取
Path Pattern: *.jpg
Cache Policy: CachingOptimized
Minimum TTL: 86400 (1 day)
Maximum TTL: 31536000 (1 year)
Default TTL: 2592000 (30 days)
Q5: CloudFront + S3 如何處理版本控制(Cache Busting)?
三種常見方案:
- 檔名加入版本號或 Hash(推薦)
- 例如:
style.v2.css,app.a3f2b1c.js - 優點:不需清除快取,新版本自動生效
- 例如:
- Query String 版本控制
- 例如:
style.css?v=2 - 需在 CloudFront 設定中啟用 Query String Forwarding
- 例如:
- 手動 Invalidation
- 使用 AWS CLI:
aws cloudfront create-invalidation --distribution-id E1234 --paths "/style.css" - 注意:每月前 1000 次 Invalidation 免費,超過部分每次 $0.005
- 使用 AWS CLI:
Q6: 如何監控 CloudFront 的成本與流量?
使用 CloudWatch 與 Cost Explorer:
- CloudWatch Metrics
Requests:總請求次數BytesDownloaded:下載流量BytesUploaded:上傳流量(POST, PUT)4xxErrorRate,5xxErrorRate:錯誤率
- 設定 CloudWatch Alarm
aws cloudwatch put-metric-alarm --alarm-name cloudfront-high-error-rate --metric-name 5xxErrorRate --namespace AWS/CloudFront --statistic Average --period 300 --threshold 5 --comparison-operator GreaterThanThreshold --evaluation-periods 2 --alarm-actions arn:aws:sns:us-east-1:123456789012:alerts - Cost Explorer 分析
- 前往 AWS Cost Explorer → 選擇 Service = CloudFront
- 分析各地區的流量費用
- 識別異常流量峰值
Q7: 如何整合 WAF 防護 CloudFront?
AWS WAF 配置步驟:
- 建立 Web ACL
- 前往 AWS WAF Console → Create Web ACL
- Resource Type:選擇 CloudFront
- 新增規則
- Rate Limiting:限制同一 IP 每 5 分鐘最多 2000 次請求
- Geo Blocking:阻擋特定國家(如需要)
- SQL Injection / XSS 防護:使用 AWS Managed Rules
- 關聯到 CloudFront
- 編輯 CloudFront Distribution → Security → AWS WAF Web ACL → 選擇剛建立的 ACL
範例:Rate Limiting 規則
{
"Name": "RateLimitRule",
"Priority": 1,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"AggregateKeyType": "IP"
}
},
"Action": {
"Block": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimitRule"
}
}
最佳實踐
安全性
- ✅ 強制 HTTPS:Viewer Protocol Policy 設為 “Redirect HTTP to HTTPS”
- ✅ 啟用 S3 版本控制:防止意外刪除或覆蓋重要檔案
- ✅ 啟用 S3 Object Lock:保護關鍵資料不被刪除(合規需求)
- ✅ 定期輪換 CloudFront Key Pair:若使用 Signed URLs,建議每年更換金鑰
- ✅ 啟用 CloudTrail:記錄所有 API 操作,便於稽核
效能優化
- ✅ 啟用 Gzip/Brotli 壓縮:在 CloudFront Behavior 中啟用 “Compress Objects Automatically”
- ✅ 使用 HTTP/2 與 HTTP/3:CloudFront 預設支援,確保瀏覽器相容
- ✅ 選擇合適的 Price Class:若主要使用者在特定地區,可選擇較少邊緣節點降低成本
- ✅ 設定合理的 TTL:根據內容更新頻率調整快取時間
成本控制
- ✅ 使用 S3 Intelligent-Tiering:自動將不常訪問的物件移至低成本儲存層
- ✅ 設定 S3 Lifecycle Policy:自動刪除過期的臨時檔案
- ✅ 監控 Invalidation 使用量:超過免費額度前考慮使用版本控制方案
- ✅ 分析流量來源:識別並阻擋異常流量(如爬蟲、攻擊)
故障排除指南
問題:CloudFront 回傳 504 Gateway Timeout
可能原因:
- S3 回應時間過長(大型檔案首次請求)
- 網路連線問題
解決方案:
- 增加 CloudFront Origin Response Timeout(預設 30 秒,可調整至 60 秒)
- 檢查 S3 存儲桶是否在高負載狀態
- 考慮使用 S3 Transfer Acceleration
問題:快取命中率(Cache Hit Ratio)過低
可能原因:
- 請求帶有過多 Query String 參數或 Headers
- TTL 設定過短
解決方案:
- 檢查 Cache Key Settings,僅轉發必要的 Query Strings 與 Headers
- 調整 Default TTL,延長快取時間
- 使用 CloudFront Cache Policy 統一管理快取規則
問題:部分使用者無法訪問(特定地區)
可能原因:
- 啟用了 Geo Restriction
- WAF 規則誤擋
解決方案:
- 檢查 CloudFront Distribution → Restrictions → Geographic Restrictions
- 檢查 WAF Web ACL Logs,確認是否有誤判規則
- 暫時將問題 IP 加入 WAF 白名單測試
總結
透過 CloudFront 訪問 S3 私有內容是 AWS 雲端架構的最佳實踐,能同時實現:
- 🚀 全球加速:低延遲、高頻寬的內容分發
- 🔒 強化安全:S3 完全私有,僅允許 CloudFront 訪問
- 💰 成本優化:減少 S3 請求費用與出站流量成本
- 🛡️ 進階防護:整合 WAF、Signed URLs、地理限制
關鍵要點:
- 新專案請使用 OAC(Origin Access Control)而非舊的 OAI
- 務必正確設定 S3 Bucket Policy,確保僅授權特定 CloudFront Distribution
- 根據內容類型設定合理的 TTL,平衡即時性與快取效益
- 啟用 CloudWatch 監控與 CloudTrail 稽核,及時發現異常
- 定期檢視 Cost Explorer,優化成本結構
這套架構適用於各種場景:媒體串流、軟體下載、付費內容、企業內部文件、API 資源等。透過正確的配置與持續優化,能打造高效、安全、經濟的雲端內容分發系統。