AWS S3 + CloudFront 静态网站托管实战:从零到全球加速完整指南
在现代 Web 架构中,静态网站托管已经成为一种高效、安全且成本极低的部署方案。
- 引言
- 架构总览与核心概念
- 环境准备与 AWS CLI 配置
- S3 存储桶配置详解
- CloudFront CDN 配置与优化
- HTTPS 与域名配置
- 自动化部署脚本
- 性能监控与故障排查
引言
在现代 Web 架构中,静态网站托管已经成为一种高效、安全且成本极低的部署方案。相比传统的服务器渲染模式,将 HTML、CSS、JavaScript 等静态资源托管在 Amazon S3 上,并通过 CloudFront CDN 进行全球加速分发,不仅能获得近乎无限的横向扩展能力,还能将运维成本降低 90% 以上。本文将带你从零开始,一步步完成一个生产级别的 S3 + CloudFront 静态网站部署,涵盖域名配置、HTTPS 证书、缓存策略和自动化部署。
无论你是前端开发者想快速上线作品集,还是 DevOps 工程师需要为团队搭建文档站点,本文提供的完整命令和配置文件都可以直接复制使用。我们将避开空洞的理论,聚焦于可操作的命令行实践和生产环境的最佳配置。读完本文后,你将掌握从域名解析到缓存策略优化的完整技能链,并拥有一套可以直接应用于生产环境的部署模板。
一、架构总览与核心概念
在动手之前,我们先理解整体架构。一个典型的 S3 + CloudFront 静态网站架构由以下组件构成:
用户请求 → Route 53(DNS) → CloudFront(CDN) → S3(源站)
↓ ↓
ACM(SSL证书) Lambda@Edge(可选)
各组件职责如下:
| 组件 | 作用 | 是否必需 |
|---|---|---|
| S3 存储桶 | 存储静态文件(HTML/CSS/JS/图片)并作为 CloudFront 源站 | ✅ 必需 |
| CloudFront | 全球 CDN 加速,缓存内容到边缘节点,提供 HTTPS 终端 | ✅ 必需 |
| ACM(证书管理器) | 提供免费的公共 SSL/TLS 证书 | ✅ 必需(HTTPS) |
| Route 53 | DNS 域名解析,将域名指向 CloudFront 分配 | 推荐 |
| Lambda@Edge | 在边缘节点执行自定义逻辑(如 URL 重写、安全头注入) | 可选 |
S3 静态网站托管 vs 传统服务器托管对比
| 维度 | S3 + CloudFront | 传统 EC2/Nginx |
|---|---|---|
| 可用性 | 99.99%(S3 SLA) | 取决于架构(单机无 SLA) |
| 全球延迟 | CloudFront 边缘节点 < 20ms | 取决于服务器地理位置 |
| 并发能力 | 近乎无限(AWS 自动扩展) | 受限于服务器配置 |
| 成本(100GB/月) | ~$15/月 | ~$30-50/月(含 EC2 + 带宽) |
| 运维负担 | 几乎为零 | 需要系统更新、监控、扩容 |
| HTTPS 配置 | ACM 一键证书 + 自动续期 | 需手动配置 Let's Encrypt 或付费证书 |
| DDoS 防护 | AWS Shield Standard(免费) | 需额外配置 |
二、环境准备与 AWS CLI 配置
2.1 确认 CLI 版本
# 检查 AWS CLI 版本(需要 v2.0+)
aws --version
# 输出示例:aws-cli/2.15.0 Python/3.11.6 Linux/x86_64
# 如未安装,使用以下命令安装
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip && sudo ./aws/install
2.2 配置访问凭证
# 交互式配置(需要 Access Key ID 和 Secret Access Key)
aws configure
# AWS Access Key ID: AKIAXXXXXXXXXXXX
# AWS Secret Access Key: xxxxxxxxxxxxxxxxxxxxxxxx
# Default region name: us-east-1
# Default output format: json
# 验证配置
aws sts get-caller-identity
# 输出应包含你的 Account ID 和 ARN
2.3 设定变量
在后续命令中,我们将使用以下环境变量,请根据实际情况替换:
export AWS_REGION="us-east-1"
export S3_BUCKET="my-static-site-$(aws sts get-caller-identity --query Account --output text)"
export DOMAIN_NAME="example.com"
三、S3 存储桶配置详解
3.1 创建存储桶并启用静态网站托管
# 创建 S3 存储桶(桶名必须全局唯一)
aws s3api create-bucket \
--bucket $S3_BUCKET \
--region $AWS_REGION \
--create-bucket-configuration LocationConstraint=$AWS_REGION
# 启用静态网站托管
aws s3api put-bucket-website \
--bucket $S3_BUCKET \
--website-configuration '{
"IndexDocument": {"Suffix": "index.html"},
"ErrorDocument": {"Key": "error.html"}
}'
3.2 配置存储桶策略(仅允许 CloudFront 访问)
这是生产环境的关键安全配置:S3 存储桶不应直接公开,而应该只允许来自 CloudFront 的请求。
# 创建存储桶策略文件
cat > /tmp/s3-bucket-policy.json << 'POLICY'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::BUCKET_NAME/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
}
}
}
]
}
POLICY
# 应用策略(等 CloudFront 创建后再替换变量并执行)
aws s3api put-bucket-policy --bucket $S3_BUCKET --policy file:///tmp/s3-bucket-policy.json
3.3 上传静态网站文件
# 假设你的网站文件在 ./dist 目录下
aws s3 sync ./dist s3://$S3_BUCKET/ \
--delete \
--cache-control "max-age=3600" \
--exclude "*.html" \
--exclude "*.json"
# HTML 文件使用较短的缓存时间(或不缓存)
aws s3 sync ./dist s3://$S3_BUCKET/ \
--delete \
--exclude "*" \
--include "*.html" \
--cache-control "no-cache,must-revalidate"
3.4 S3 高级配置:版本控制与生命周期策略
# 启用版本控制(保护文件不被误删)
aws s3api put-bucket-versioning \
--bucket $S3_BUCKET \
--versioning-configuration Status=Enabled
# 配置生命周期策略(旧版本自动清理)
aws s3api put-bucket-lifecycle-configuration \
--bucket $S3_BUCKET \
--lifecycle-configuration '{
"Rules": [{
"Id": "cleanup-old-versions",
"Status": "Enabled",
"NoncurrentVersionExpiration": {
"NoncurrentDays": 30
},
"AbortIncompleteMultipartUpload": {
"DaysAfterInitiation": 7
}
}]
}'
四、CloudFront CDN 配置与优化
4.1 创建 CloudFront 分配
# 创建 CloudFront 分发配置文件
cat > /tmp/cloudfront-config.json << 'EOF'
{
"CallerReference": "static-site-20260720",
"Aliases": {
"Quantity": 1,
"Items": ["www.example.com"]
},
"DefaultRootObject": "index.html",
"Origins": {
"Quantity": 1,
"Items": [{
"Id": "S3-origin",
"DomainName": "BUCKET_NAME.s3-website-us-east-1.amazonaws.com",
"CustomOriginConfig": {
"HTTPPort": 80,
"HTTPSPort": 443,
"OriginProtocolPolicy": "http-only",
"OriginSslProtocols": {
"Quantity": 1,
"Items": ["TLSv1.2"]
}
}
}]
},
"DefaultCacheBehavior": {
"TargetOriginId": "S3-origin",
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"],
"CachedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"]
}
},
"Compress": true,
"CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6"
},
"PriceClass": "PriceClass_100"
}
EOF
# 创建分发
aws cloudfront create-distribution \
--distribution-config file:///tmp/cloudfront-config.json \
--query 'Distribution.Id' \
--output text
4.2 CloudFront 缓存策略深度优化
CloudFront 提供了多种缓存策略选项,合理配置能大幅提升命中率并降低源站负载:
| 缓存策略 | 适用场景 | TTL 配置建议 |
|---|---|---|
| Managed-CachingOptimized | 图片、CSS、JS 等不常变的静态资源 | 最小 TTL: 1s, 默认 TTL: 86400s, 最大 TTL: 31536000s |
| Managed-CachingDisabled | 需要实时更新的内容(如 API 响应) | 按需自定义 |
| 自定义缓存策略 | HTML 页面(需要兼顾缓存和新鲜度) | 最小 TTL: 0s, 默认 TTL: 300s, 最大 TTL: 600s |
# 创建自定义缓存策略(适用于 HTML 页面)
aws cloudfront create-cache-policy \
--cache-policy-config '{
"Name": "StaticSiteHTMLPolicy",
"DefaultTTL": 300,
"MinTTL": 0,
"MaxTTL": 600,
"ParametersInCacheKeyAndForwardedToOrigin": {
"EnableAcceptEncodingGzip": true,
"EnableAcceptEncodingBrotli": true,
"HeadersConfig": {
"HeaderBehavior": "whitelist",
"Headers": {"Quantity": 1, "Items": ["CloudFront-Viewer-Country"]}
},
"CookiesConfig": {"CookieBehavior": "none"},
"QueryStringsConfig": {"QueryStringBehavior": "none"}
}
}'
4.3 CloudFront 价格等级选择
CloudFront 提供三个价格等级,适合不同预算和覆盖需求:
| 价格等级 | 边缘节点覆盖 | 适用场景 | 月度成本(1TB) |
|---|---|---|---|
| Price Class 100 | 北美 + 欧洲 | 目标用户集中于欧美 | ~$85 |
| Price Class 200 | 100类 + 亚洲、中东、非洲部分地区 | 全球业务但预算有限 | ~$110 |
| Price Class All | 全球所有边缘节点 | 需要最佳全球性能 | ~$130 |
4.4 自定义错误页面
# 配置 SPA (Single Page Application) 的错误响应
# 将所有 404/403 错误重定向到 index.html
aws cloudfront update-distribution \
--id $DISTRIBUTION_ID \
--distribution-config '{
...,
"CustomErrorResponses": {
"Quantity": 2,
"Items": [
{
"ErrorCode": 403,
"ResponsePagePath": "/index.html",
"ResponseCode": "200",
"ErrorCachingMinTTL": 10
},
{
"ErrorCode": 404,
"ResponsePagePath": "/index.html",
"ResponseCode": "200",
"ErrorCachingMinTTL": 10
}
]
}
}'
五、HTTPS 与域名配置
5.1 申请 ACM 公共证书
# 在 us-east-1 申请证书(CloudFront 要求证书在 us-east-1)
aws acm request-certificate \
--domain-name www.example.com \
--subject-alternative-names example.com \
--validation-method DNS \
--region us-east-1
# 获取 DNS 验证记录
aws acm describe-certificate \
--certificate-arn $CERT_ARN \
--query 'Certificate.DomainValidationOptions[0].ResourceRecord' \
--region us-east-1
5.2 Route 53 DNS 配置
# 创建 A 记录别名指向 CloudFront
aws route53 change-resource-record-sets \
--hosted-zone-id $HOSTED_ZONE_ID \
--change-batch '{
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "Z2FDTNDATAQYW2",
"DNSName": "CLOUDFRONT_DOMAIN_NAME",
"EvaluateTargetHealth": false
}
}
}]
}'
六、自动化部署脚本
将上述步骤整合为可复用的部署脚本,实现一键部署与更新:
#!/bin/bash
# deploy-static-site.sh — S3 + CloudFront 静态网站自动化部署
set -euo pipefail
# ============ 配置区 ============
DIST_DIR="./dist"
S3_BUCKET="my-static-site-prod"
CLOUDFRONT_ID="E1ABCDEFGHIJKL"
AWS_REGION="us-east-1"
echo "🚀 开始部署静态网站..."
# 1. 构建项目(如需要)
if [ -f "package.json" ]; then
echo "📦 执行项目构建..."
npm run build
fi
# 2. 上传到 S3
echo "📤 上传静态文件到 S3..."
# 长缓存资源(带 hash 的文件名)
aws s3 sync $DIST_DIR s3://$S3_BUCKET/ \
--delete \
--exclude "*.html" \
--exclude "service-worker.js" \
--exclude "asset-manifest.json" \
--cache-control "public,max-age=31536000,immutable"
# 短缓存资源(HTML 等入口文件)
aws s3 sync $DIST_DIR s3://$S3_BUCKET/ \
--delete \
--exclude "*" \
--include "*.html" \
--include "service-worker.js" \
--cache-control "public,max-age=0,must-revalidate"
# 3. 失效 CloudFront 缓存(使旧内容立即过期)
echo "🔄 创建 CloudFront 缓存失效..."
INVALIDATION_ID=$(aws cloudfront create-invalidation \
--distribution-id $CLOUDFRONT_ID \
--paths "/*" \
--query 'Invalidation.Id' \
--output text)
# 4. 等待失效完成
echo "⏳ 等待缓存失效完成..."
aws cloudfront wait invalidation-completed \
--distribution-id $CLOUDFRONT_ID \
--id $INVALIDATION_ID
echo "✅ 部署完成!网站已更新。"
echo "🌐 访问地址: https://www.example.com"
七、性能监控与故障排查
7.1 CloudFront 关键指标监控
# 查看 CloudFront 分发的近期请求统计
aws cloudfront get-distribution \
--id $CLOUDFRONT_ID \
--query 'Distribution.DistributionConfig.Enabled'
# 通过 CloudWatch 查看缓存命中率
aws cloudwatch get-metric-statistics \
--namespace AWS/CloudFront \
--metric-name CacheHitRate \
--dimensions Name=DistributionId,Value=$CLOUDFRONT_ID \
--start-time $(date -d '24 hours ago' -u +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--period 3600 \
--statistics Average
7.2 常见问题与解决方案
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
| 返回 403 Access Denied | S3 存储桶策略未授权 CloudFront | aws s3api get-bucket-policy --bucket $BUCKET | 检查 OAC/OAI 配置 |
| 更新文件后仍然看到旧内容 | CloudFront 缓存未失效 | aws cloudfront list-invalidations --distribution-id $ID | 创建失效请求或使用文件版本化 |
| HTTPS 证书错误 | ACM 证书未在 us-east-1 申请 | aws acm list-certificates --region us-east-1 | 在 us-east-1 重新申请证书 |
| 首次访问慢 | 边缘节点未缓存(冷启动) | 查看 x-cache: Miss from cloudfront 响应头 | 进行预热或接受首次 Miss 延迟 |
| 跨域请求被拦截 | 缺少 CORS 响应头 | curl -I https://cdn.example.com/file.js | 在 S3 上配置 CORS 规则 |
7.3 S3 CORS 配置
当前端应用需要直接从浏览器访问 S3 上的资源(例如通过 JavaScript Fetch API 加载 JSON 数据或字体文件),必须正确配置跨域资源共享策略,否则浏览器会阻止这些跨域请求。以下是完整的配置方法。
# 如果前端需要跨域访问 S3 资源
aws s3api put-bucket-cors --bucket $S3_BUCKET --cors-configuration '{
"CORSRules": [{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedOrigins": ["https://www.example.com"],
"ExposeHeaders": ["ETag", "x-amz-meta-custom-header"],
"MaxAgeSeconds": 3600
}]
}'
八、成本分析与优化建议
月度成本明细(中小型网站,100GB 流量/月)
| 费用项 | 用量 | 单价 | 月费用 |
|---|---|---|---|
| S3 存储(Standard) | 5GB | $0.023/GB | $0.12 |
| S3 PUT/COPY 请求 | 1,000 次 | $0.005/千次 | $0.01 |
| S3 GET 请求 | 10,000 次 | $0.0004/千次 | $0.004 |
| CloudFront 数据传输 | 100GB | $0.085/GB(美国) | $8.50 |
| CloudFront HTTP 请求 | 500,000 次 | $0.0075/万次 | $0.38 |
| Route 53 托管区 | 1 个 | $0.50/月 | $0.50 |
| ACM 证书 | 1 个 | 免费 | $0.00 |
| 合计 | ~$9.51/月 |
优化建议
除了上述基本的成本控制手段之外,以下进阶优化措施能够帮助你在大流量场景下进一步压缩开支,同时提升用户体验:
- 启用 Gzip/Brotli 压缩:已在 CloudFront 配置中启用,可减少 60-80% 传输量
- 使用 S3 Intelligent-Tiering:如果文件访问模式不确定,可自动优化存储成本
- 合理设置缓存 TTL:静态资源设置较长 TTL(一年),HTML 设置较短 TTL
- 启用 Origin Shield:在高流量场景下,增加一层集中缓存减少回源请求,可将回源请求降低至原来的十分之一
- 按需选择 Price Class:如果用户主要集中在特定地区,选择对应的价格等级可以有效降低边缘传输成本
- 使用 Lambda@Edge 进行响应压缩:对于动态生成的内容,可以在边缘节点实时压缩后再返回给用户
九、总结与最佳实践
通过本文的完整实践,我们成功搭建了一个基于 S3 + CloudFront + ACM + Route 53 的生产级静态网站托管方案。这个架构不仅成本低廉(月均不到 10 美元),而且性能卓越,借助 CloudFront 全球 400 多个边缘节点,无论用户身处哪个大洲都能获得低延迟的访问体验。回顾整个过程,以下最佳实践值得你在实际项目中坚持:
- S3 绝不直接公开:始终通过 CloudFront OAC(Origin Access Control)代理 S3 请求,避免存储桶直接暴露在公网
- 文件版本化 + 缓存失效组合:对静态资源使用内容哈希命名(如
main.abc123.js),结合Cache-Control: immutable实现永久缓存;HTML 文件使用短 TTL 或must-revalidate - 监控缓存命中率:将 CloudFront 的
CacheHitRate指标接入 CloudWatch Dashboard,设定低于 85% 的告警阈值 - 自动化部署:将部署流程集成到 CI/CD 流水线中,每次代码提交自动触发 S3 同步和 CloudFront 失效
- 成本可控:中小型网站每月成本不到 10 美元,随着流量增长成本呈线性增长而非指数级
AWS S3 与 CloudFront 的组合不仅是静态网站托管的最优解,也是现代 Jamstack 架构的基石。掌握这套技术栈,你将能够以极低的成本、极高的可靠性为数百万用户提供闪电般的网页加载体验。
本文由 6.chengzicloud.cloud 提供,点击访问首页了解更多