S3 存储文件,CloudFront 通过 HTTPS 在全球范围内分发,ACM 免费签发证书,Route 53 让 mybrand.com 指向整个系统。这是在 AWS 上部署静态网站的标准方案——一旦搭建完成,每次部署只需一条 aws s3 sync 命令。
现代的主流替代方案——Vercel、Netlify、Cloudflare Pages——已将整条部署流水线打包成一个按钮。推送 git 仓库,它们就会自动处理存储、CDN、证书和 DNS。人们仍然手动搭建 S3 + CloudFront 的原因在于,它属于 AWS 生态:同一套 IAM 策略可以让 Lambda 把生成的报告写入同一个 bucket;同一个 CloudFront 分发可以前置于其身后的 ALB;同一个 Route 53 托管区可以管理其他 AWS 资源的记录。如果你的基础设施在 AWS 上,静态网站也理应在 AWS 上——而且静态内容的 AWS 价格极为低廉。
核心概念:S3 是源站(私有 bucket,存放构建产物);CloudFront 是缓存与入口(在全球边缘节点缓存文件、终止 TLS、提供 https:// 访问);ACM 提供证书(免费 TLS,但 CloudFront 要求证书必须位于 us-east-1);Route 53 管理 DNS(Alias 记录将域名指向 CloudFront 分发)。四项服务各司其职,互不越界。
本教程将从头到尾完整介绍首次配置流程,以及流水线搭建完成后每次部署的操作方式。对于控制台操作繁琐的步骤(bucket 策略、sync),我们使用 AWS CLI;对于 CLI 操作繁琐的步骤(CloudFront、ACM),我们使用控制台。最终,部署只需三条命令:npm run build && aws s3 sync ./dist s3://mybrand-site && aws cloudfront create-invalidation ...,从此一劳永逸。
us-east-1aws configure)、域名的 DNS 托管于 Route 53(或愿意在其他地方添加记录),以及一个已构建好的静态网站(一个包含 HTML/CSS/JS 的目录)。
选择一个 bucket 名称。它必须在整个 AWS 范围内全局唯一,建议使用类似 mybrand-site-prod-2026 的命名。名称本身并不重要——用户永远不会看到它,因为 CloudFront 在前面挡着。
aws s3api create-bucket \
--bucket mybrand-site-prod-2026 \
--region us-east-1
# (对于 us-east-1 以外的区域,需添加 --create-bucket-configuration LocationConstraint=<region>)
保留默认的"屏蔽所有公开访问"设置。有了 CloudFront + OAC 在前面(第 4 步),bucket 保持私有状态;CloudFront 使用自己的凭证来读取文件。公开 bucket 的方式仍然可行,但已是过时的模式——AWS 新文档全面推荐 OAC。
aws s3 sync 就是部署命令如果你的网站构建输出目录是 ./dist(或 ./build、./public,取决于项目):
aws s3 sync ./dist s3://mybrand-site-prod-2026 --delete
# --delete 会删除 S3 中本地已不存在的文件——
# 不加此参数,已删除的页面会作为孤儿继续在线。
首次 sync 会上传所有文件,后续只上传变更的文件。这就是你之后要脚本化的部署命令。此时 S3 已有文件,但无法访问——bucket 是私有的,CloudFront 也还没建。
这是最常见的首次配置错误。AWS Certificate Manager 证书是按区域划分的。CloudFront 是全球服务,但证书查找固定走 us-east-1。申请证书前,请将控制台区域切换到"美国东部(弗吉尼亚北部)— us-east-1",或通过 CLI 操作:
aws acm request-certificate \
--domain-name mybrand.com \
--subject-alternative-names www.mybrand.com \
--validation-method DNS \
--region us-east-1
ACM 会返回一个 CertificateArn 以及一组 CNAME 验证记录——证书中每个域名各一条。如果你的 DNS 在同一账号的 Route 53 上,控制台会提供一键"在 Route 53 中创建记录"的按钮。否则,需要手动将每条 CNAME 复制到你的 DNS 服务商。记录生效后,验证通常在几分钟内完成。
打开 CloudFront → 创建分发,填写以下内容:
mybrand.com 和 www.mybrand.com。index.html。点击创建。CloudFront 开始部署分发,首次大约需要 5 到 15 分钟。你会得到一个类似 d1234abcde.cloudfront.net 的分发域名——在 DNS 接入之前,可以直接用该 URL 测试。
OAC 创建流程会在成功页面显示 bucket 策略片段,复制它。在 S3 → bucket → 权限 → Bucket 策略中粘贴。内容如下:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": { "Service": "cloudfront.amazonaws.com" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mybrand-site-prod-2026/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789:distribution/E1ABCDEFGHIJK"
}
}
}]
}
SourceArn 条件是 OAC 安全性的关键:只有你指定的分发才能读取这个 bucket。若没有此条件,任何账号下的任何 CloudFront 分发都可以读取。
在 Route 53 → 你的托管区 → 创建记录:
www(对应 www 子域名)。创建完成后,重复以上步骤配置另一个域名(根域名 / www)。DNS 解析通常在几分钟内生效——如果是同一 AWS 账号下的 Alias 记录,有时几秒就能完成。
如果你的 DNS 不在 Route 53,可在你的 DNS 服务商添加常规 CNAME 记录,将 www 指向 d1234abcde.cloudfront.net,并使用你的 DNS 服务商提供的 CNAME 扁平化 / ALIAS 功能处理根域名(详见 DNS 教程)。
# 1. 确认证书已附加到分发。
curl -sI https://mybrand.com | grep -E '^(HTTP|Server)'
# 预期结果:HTTP/2 200 ... Server: CloudFront
# 2. 确认文件可以访问。
curl -s https://mybrand.com | head
# 预期结果:你的 index.html 内容。
# 3. 确认 DNS 指向 CloudFront。
dig mybrand.com +short
# 预期结果:CloudFront 边缘节点 IP(各区域不同;确认不为空即可)。
从现在起,部署的方式如下——将这三行保存为项目中的 deploy.sh:
#!/usr/bin/env bash
set -e
npm run build
aws s3 sync ./dist s3://mybrand-site-prod-2026 --delete
aws cloudfront create-invalidation \
--distribution-id E1ABCDEFGHIJK \
--paths "/*"
缓存失效(invalidation)是让新文件立即生效的关键。不执行失效操作,CloudFront 会继续提供缓存中的旧文件,直到 TTL 过期(缓存响应默认 24 小时)。每月有 1,000 个失效路径免费——以每次部署一个路径的失效来算,相当于每天 33 次,足以覆盖大多数项目。
以每月 10,000 访客、约 1 GB 流量的个人网站为例:
总计:一个真实但流量平静的个人网站,每月不到 $1。AWS 免费套餐的前 12 个月提供 50 GB 免费 CloudFront 出站流量,通常能覆盖所有费用;免费期结束后,费用线性增长。若静态网站月流量达到 1 TB,传输费用约为 $85——这时候 Cloudflare Pages(带宽无限、免费)就显得非常有吸引力了。