通过CloudFront转发带V4签名的PUT请求至S3时遇400/403错误
解决CloudFront转发S3 PUT请求的AWS V4签名不匹配问题
问题背景
直接用Postman向S3发送带AWS V4签名的PUT请求(示例如下)可以正常上传:
PUT https://example.com/to-process/test.json?X-Amz-Expires=86400&X-Amz-Date=20240415T033843Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIATGD6OWCXBG3WY4FF%2F20240415%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=766f0a30fb6702597706d1cb8d88f9f8ea2de2334acf7ecdfce51b0bfdf07412
但通过配置了多源站转发的CloudFront(部分路径转EC2,部分转S3)发送请求时,先出现400错误,后转为403 SignatureDoesNotMatch错误。核心矛盾是:签名时用了CloudFront自定义域名作为Host,而CloudFront转发到S3时,S3用自身桶端点验证签名,导致签名不匹配。
解决方案
方案1:调整CloudFront源站请求策略,保证Host头一致性
- 创建自定义源站请求策略
- 弃用默认的
AllViewerExceptHostHeader,改为勾选「转发所有查看器请求头」,或明确指定包含Host头 - 确保策略保留所有
X-Amz-*开头的签名查询参数
- 弃用默认的
- 关联策略到S3行为规则
将自定义策略绑定到CloudFront中指向S3的路径行为上 - 统一签名时的Host头
生成AWS V4签名时,使用S3桶的原生端点作为Host头(例如your-bucket.s3.eu-west-1.amazonaws.com),请求URL仍使用CloudFront自定义域名
方案2:使用CloudFront Origin Access Control(OAC)简化鉴权
由于S3桶开启了Block all public access,更安全的方式是让CloudFront直接和S3鉴权,避免查看端直接对S3签名:
- 在CloudFront控制台创建OAC,关联目标S3源站
- 给OAC配置S3桶的权限(例如
s3:PutObject、s3:GetObject等) - 将OAC绑定到CloudFront的S3行为规则上
- 查看端直接向CloudFront发送请求,无需附加S3签名,由CloudFront负责和S3的身份验证,彻底规避签名不匹配问题
方案3:配置S3支持自定义域名签名
如果必须让查看端基于CloudFront域名生成签名:
- 给S3桶配置自定义域名(即你的CloudFront域名
example.com),可通过桶别名或静态网站托管实现 - 生成签名时,使用CloudFront自定义域名作为Host头
- 确保CloudFront转发请求时保留该Host头,S3会基于自定义域名验证签名,与你生成的签名匹配
验证步骤
- 查看CloudFront访问日志,确认转发到S3的请求中Host头与签名时使用的一致
- 用
curl -v发送测试请求,检查请求头中的Host字段是否正确传递 - 核对签名生成时的关键参数(
X-Amz-Date、区域、服务、签名头列表)是否与实际请求完全一致
内容的提问来源于stack exchange,提问作者DaveB
相关产品推荐
相关产品推荐

