CloudFront 使用 Amazon S3 作为源站时返回 403,不一定只是“存储桶没有公开”。常见原因包括 OAC 没有绑定到正确的源、存储桶策略中的分配 ARN 写错、源站类型选择错误、对象名称或路径不存在、默认根对象配置不正确、SSE-KMS 权限缺失,以及旧的 403 响应仍保留在 CloudFront 缓存中。
正确的处理方式不是立即关闭 S3 Block Public Access,而是先判断 403 发生在哪一层,再检查 CloudFront 到 S3 的私有访问链路。对于私有 S3 源,推荐保持存储桶不公开,并使用 Origin Access Control(OAC)授权指定的 CloudFront 分配读取对象。
本文配置核查日期为 2026 年 7 月 31 日。命令和策略中的账号、存储桶、分配 ID、域名及对象路径均为占位符,使用时必须替换为自己的值。
先判断:是哪一种 403?
先用下面的现象缩小范围,比反复修改权限更有效。
所有文件都返回 403:优先检查源站类型、OAC 绑定、S3 存储桶策略、显式 Deny 和 KMS 密钥策略。
只有根域名返回 403,但 /index.html 正常:优先检查 CloudFront 默认根对象。
只有某个文件返回 403:检查对象是否存在、大小写、前缀路径、对象所有权和加密方式。
权限修复后仍返回旧 403:检查错误缓存 TTL,并对对应对象执行缓存失效。
只有部分用户返回 403:检查地理限制、AWS WAF、签名 URL、签名 Cookie 和查看器访问限制。
可先分别请求根路径和一个确定存在的对象:
curl -I https://cdn.example.com/ curl -I https://cdn.example.com/index.html curl -I https://cdn.example.com/assets/app.css
重点记录状态码、server、x-cache、via 和 x-amz-cf-id。如果根路径失败而具体对象成功,通常不应先改存储桶权限。
第一步:确认使用的是普通 S3 源还是网站端点
这是最容易混淆的配置之一。CloudFront 中常见的 S3 源有两种:
普通 S3 存储桶 REST 端点:可以使用 OAC,适合私有存储桶,也是本文重点讨论的方式。
S3 静态网站端点:必须在 CloudFront 中作为自定义源使用,不能使用 OAC 或 OAI;网站端点本身还要求内容可公开读取。
如果目标是“用户只能通过 CloudFront 访问文件,不能直接访问 S3”,应选择普通 S3 存储桶源并配置 OAC,而不是选择 S3 网站端点。
不要为了修复 403 直接关闭 Block Public Access。 如果原本设计的是私有 S3 源,公开存储桶可能掩盖 OAC 配置错误,并扩大数据暴露范围。
第二步:检查 OAC 是否绑定到正确的 CloudFront 源
创建 OAC 并不等于已经启用。还需要把它绑定到当前分配实际使用的 S3 源。
进入 CloudFront 控制台并选择对应分配。
打开“源”页面,选择实际的 S3 源并编辑。
在源访问设置中选择 Origin Access Control。
确认选择的是正确的 OAC,而不是仅创建但未绑定的 OAC。
签名行为优先使用“始终签署请求(推荐)”。
保存后等待分配状态完成部署,再重新测试。
使用推荐的“始终签名”设置时,CloudFront 会使用 SigV4 为发送到 S3 的源请求签名。若选择“不签名”,私有存储桶将无法通过该 OAC 正常访问。
第三步:检查 S3 存储桶策略
OAC 模式下,存储桶策略通常需要允许 CloudFront 服务主体执行 s3:GetObject,同时使用 AWS:SourceArn 将权限限制到指定分配。
只读静态内容可以从下面的最小策略开始:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontReadOnly",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::YOUR_ACCOUNT_ID:distribution/YOUR_DISTRIBUTION_ID"
}
}
}
]
}检查时重点关注以下位置:
Resource 是否包含
/*:读取对象需要匹配对象 ARN,而不只是存储桶 ARN。账号 ID 和分配 ID 是否正确:不要把 OAC ID、源 ID 或分配域名误填到 SourceArn。
Principal 是否正确:OAC 使用 CloudFront 服务主体,不是旧版 OAI 的 IAM ARN。
是否存在显式 Deny:组织 SCP、权限边界、VPC 端点策略、存储桶策略中的 Deny 都可能覆盖 Allow。
是否限制了前缀:如果 Resource 只允许
public/*,请求其他目录仍会被拒绝。
如果正在从 OAI 迁移到 OAC,可以在部署过渡期同时保留 OAI 和 OAC 的读取授权;确认新配置已在所有边缘节点部署后,再删除旧 OAI 权限。这样可以减少切换过程中出现 403 的概率。
第四步:确认对象存在,路径与大小写完全一致
S3 对象键区分大小写。index.html、Index.html 和 INDEX.HTML 是三个不同的对象。URL 中多一个目录、少一个前缀或大小写不同,都可能表现为 AccessDenied。
可使用 AWS CLI 检查对象是否存在:
aws s3api head-object \ --bucket YOUR_BUCKET_NAME \ --key index.html
如果使用了 CloudFront 的 Origin Path,还要把它计算进最终对象键。例如源路径配置为 /production,查看器请求 /index.html 时,CloudFront 实际可能访问 S3 中的 production/index.html。
还应检查 S3 Object Ownership。AWS 对新的存储桶默认使用 Bucket owner enforced,并禁用 ACL。OAC 场景通常应使用此设置并通过存储桶策略管理访问;如果对象由其他账号上传且仍依赖旧 ACL,所有权不一致也可能造成访问问题。
第五步:根路径 403 时检查默认根对象
如果 https://cdn.example.com/index.html 正常,但 https://cdn.example.com/ 返回 403,最可能的问题不是 OAC,而是没有设置默认根对象,或者填写格式错误。
在 CloudFront 分配的常规设置中,将默认根对象设置为:
index.html
不要填写:
/index.html
AWS 文档明确说明,默认根对象不能以正斜杠开头;使用 /index.html 可能导致 403 Access Denied。
默认根对象只自动处理分配根路径。假设默认根对象是 index.html,请求 /docs/ 时,CloudFront 不会自动改为 /docs/index.html。需要目录索引行为时,可以使用 CloudFront Function 重写 URI,或在应用构建阶段生成明确路径,但应先测试缓存键和重定向逻辑。
第六步:SSE-KMS 加密对象还需要密钥策略
如果 S3 对象使用 SSE-S3 加密,通常不需要额外的 KMS 权限;如果对象使用客户管理的 AWS KMS 密钥进行 SSE-KMS 加密,仅允许 s3:GetObject 可能仍然返回 403。
此时需要在 KMS 密钥策略中授权 CloudFront 服务主体,并用具体分配 ARN 限定来源。常见所需操作包括 kms:Decrypt,涉及通过 CloudFront 写入对象时还可能需要 kms:Encrypt 和 kms:GenerateDataKey*。
排查方法:先查看出错对象的 Server-side encryption 字段。如果只有部分文件 403,而这些文件恰好使用了不同的 KMS 密钥,应优先检查对应密钥策略。
第七步:权限修复后清理已缓存的 403
CloudFront 会缓存部分错误响应。默认情况下,错误响应通常缓存 10 秒;如果自定义错误响应设置了更长的 Error Caching Minimum TTL,或者源站错误响应携带较长的 Cache-Control,权限修复后仍可能继续看到旧的 403。
先等待配置部署和错误缓存过期。如果需要立即验证,可以对具体路径创建缓存失效:
aws cloudfront create-invalidation \ --distribution-id YOUR_DISTRIBUTION_ID \ --paths "/index.html"
只失效实际出错的对象,通常比直接使用 /* 更稳妥。若大量对象都缓存了错误,再根据影响范围决定是否扩大失效路径。
对于 S3 源,即使把错误缓存最小 TTL 设置为 0,CloudFront 对 S3 源错误仍可能保留至少 1 秒,以帮助保护源站。
其他会让 CloudFront 返回 403 的配置
当 S3 权限和对象路径都正确时,还要检查 CloudFront 自身是否主动拒绝了请求:
AWS WAF:规则、速率限制或托管规则组可能拦截请求。
地理限制:被限制国家或地区的用户会收到 403。
签名 URL 或签名 Cookie:启用 Restrict viewer access 后,缺少有效签名的请求会返回 403。
错误的 Host 头转发:向 S3 REST API 端点转发不匹配的查看器 Host 头可能导致错误。
CloudFront 套 CloudFront:AWS 不建议把多个 CloudFront 分配串联,堆叠分配可能返回 403。
不允许的 HTTP 方法:行为只允许 GET/HEAD 时,PUT、POST 或 DELETE 请求不会按预期到达 S3。
推荐的 CloudFront S3 403 排查顺序
请求根路径和一个确定存在的文件,确认故障范围。
确认对象键存在,并检查路径、大小写和 Origin Path。
确认源是普通 S3 存储桶端点,而不是误用网站端点。
确认 OAC 已绑定到当前源,并使用推荐的始终签名设置。
核对存储桶策略中的 Principal、Resource、账号 ID 和分配 ID。
检查显式 Deny、对象所有权和跨账号上传问题。
如果使用 SSE-KMS,检查对应 KMS 密钥策略。
根路径单独失败时,检查默认根对象且不要添加开头斜杠。
检查 WAF、地理限制、签名访问和 HTTP 方法设置。
等待分配部署完成,并清除必要的错误缓存。
常见问题
修复 CloudFront 403 必须公开 S3 存储桶吗?
不需要。普通 S3 存储桶可以保持 Block Public Access,通过 OAC 和限定具体分配 ARN 的存储桶策略授权 CloudFront 读取对象。
OAC 和 OAI 应该选哪个?
新配置优先使用 OAC。AWS 推荐 OAC,因为它支持所有 S3 区域、SSE-KMS 和动态 S3 请求等能力。OAI 属于旧方式,主要用于兼容已有配置。
为什么 S3 控制台里能看到文件,CloudFront 仍然返回 403?
控制台访问使用的是当前登录用户的 IAM 权限,CloudFront 访问使用 OAC 签名和存储桶策略。两者身份不同,因此“管理员能看到文件”不能证明 CloudFront 已获得读取权限。
为什么不存在的 S3 文件有时返回 403,而不是 404?
私有 S3 源不会总是向请求方暴露对象是否存在。AWS 的 CloudFront 故障排查文档也指出,缺少对象或对象名称大小写错误可能表现为 Access Denied。
设置 index.html 后,为什么 /docs/ 仍然不显示 docs/index.html?
CloudFront 默认根对象主要用于分配根 URL,不会自动为每个子目录查找 index.html。子目录索引需要单独的 URI 重写、应用路由或静态站点方案。
修改权限后需要多久生效?
S3 和 KMS 策略修改通常很快,但 CloudFront 分配配置需要完成全球部署,旧的 403 还可能受错误缓存 TTL 影响。应先等待部署完成,再根据需要失效具体对象。
结论
CloudFront 访问 S3 返回 403 时,应先区分“所有对象失败、根路径失败、单个对象失败还是旧错误被缓存”。对于私有内容,正确架构通常是普通 S3 存储桶、开启 Block Public Access、使用 OAC 始终签名,并在存储桶策略中只允许指定 CloudFront 分配读取对象。
如果具体文件正常而根路径失败,优先修复默认根对象;如果只有加密文件失败,检查 KMS 密钥策略;如果权限已经修好但仍报错,等待错误缓存过期或失效对应路径。按照这个顺序排查,可以避免通过公开存储桶来“临时解决”权限问题。
CloudFlew 提供基于 AWS CloudFront 的 CDN 服务。部署私有 S3 源时,应同时核对 CloudFront 源配置、OAC、S3 存储桶策略、默认根对象和缓存行为,避免只处理其中一个环节。
参考资料
AWS CloudFront 开发者指南:限制对 Amazon S3 源的访问,核查日期:2026-07-31
AWS CloudFront 开发者指南:指定默认根对象,核查日期:2026-07-31
AWS CloudFront 开发者指南:排查分配问题,核查日期:2026-07-31
AWS CloudFront 开发者指南:HTTP 403 Permission Denied,核查日期:2026-07-31
AWS CloudFront 开发者指南:控制错误缓存时间,核查日期:2026-07-31