OSS 签名报 403 的常见原因:Date 头与资源路径的坑
OSS 签名 403 应先看返回错误码区分签名错误、时间偏移或权限问题;SignatureDoesNotMatch 时重点排查 Date 头是否符合 GMT 格式、UTC 时区、时间同步及代理改写,以及 CanonicalizedResou
看完这篇,你能在十分钟内自己判断 OSS 签名 403 到底是 Date 头写错了,还是资源路径对不上,而不是反复换 SDK、反复贴同一段代码问人。
第一步:先看清 403 里的错误码,别急着改代码
403 只是一个 HTTP 状态码,OSS 返回的 XML 里才有真答案。用 curl 打一次请求,看 <Code> 字段:
curl -i "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/test.txt" -H "Authorization: OSS ..."
SignatureDoesNotMatch:签名算错了,本文主角,继续往下看。RequestTimeTooSkewed:客户端时间和 OSS 服务器差了超过 15 分钟,直接查时间同步。AccessDenied:签名其实是对的,是权限、IP 白名单、Referer 或防盗链拦的,别浪费时间改签名。InvalidAccessKeyId:AccessKeyId 不存在或被禁用。
注意:不要拿 403 的 status code 当唯一线索。同一个 403 背后有四种完全不同的原因,错误码不同,改的方向完全不同。
第二步:Date 头的四个坑
签名串里的 Date,必须和你 HTTP 请求头里实际发送的 Date 完全一致。常见错误:
1. 格式写错。 必须是 HTTP-date 格式的 GMT 字符串,例如:
Wed, 21 Oct 2015 07:28:00 GMT
不是 2025-01-01 12:00:00,不是 ISO8601 带毫秒,也不是本地时间不带时区后缀。
2. 时区没转 UTC。 服务器在 +08:00,直接 date 出来的是北京时间,直接塞进签名就是错的。生成时用:
date -u '+%a, %d %b %Y %H:%M:%S GMT'
3. 客户端和服务器时间漂移。 虚拟机挂起后恢复、Docker 容器没继承宿主时间、树莓派没装 RTC,都会导致时间慢几分钟到几小时。检查:
date -u
ntpdate -q ntp.aliyun.com
注意:不能只把本地时间「手动调准一次」。容器重启、宿主迁移后还会漂,正确做法是让宿主机跑 NTP/chrony,容器共享宿主时间。
4. 中间层把 Date 头改了。 Nginx 反代、CDN、API 网关默认会自己补一个 Date 响应头,某些代理也会覆写请求头。如果你的服务端在代理后面,先直连 OSS 复现一次,确认是不是代理动的手。
另外,用 V4 签名时会用 x-oss-date 头。如果你同时传了 Date 和 x-oss-date,两者不一致,照样 403。
第三步:资源路径(CanonicalizedResource)的五个坑
签名串最后一段是资源路径,规则是「跟请求实际走的那条路一致」,不是「你觉得它应该是什么」。
1. Bucket 前缀要不要带。 走路径风格 oss-cn-hangzhou.aliyuncs.com/bucket/object 时,签名里要带 /bucket/object;走 bucket 域名或 CNAME 自定义域名时,规则不同(CNAME 场景通常不带 BucketName)。最稳的验证方式是:用官方 SDK 生成一次同样的请求,把它内部拼出的 StringToSign 打印出来,和自己的对比。
2. 是否 URL 编码。 这是最高频的坑:URL 里必须出现 %E4%B8%AD 这类编码,而签名串里用的是原始 ObjectKey 还是编码后的,取决于你用的是 V1 还是 V4 签名,两者规则不同。混着抄示例代码,必错。
3. 空格和加号。 路径里的空格要编码成 %20,写成 + 就变成另一个 key 了。
4. 多余和缺失的斜杠。 ObjectKey 是 a/b.txt,签名里写成 //a/b.txt 或漏掉前导斜杠,都会 SignatureDoesNotMatch。大小写也一样,OSS 的 key 是大小写敏感的。
5. Query 参数没参与签名。 ?acl、?uploads、?response-content-disposition=... 这类子资源和覆盖响应头参数,必须按规则参与签名,少一个就报错。
注意:如果你的请求经过自建 Nginx 转发,检查
location和proxy_pass末尾的斜杠。proxy_pass http://oss/;和proxy_pass http://oss;传给后端的路径不一样,会直接改写资源路径,签名自然对不上。
第四步:把 StringToSign 打出来,一眼定位
阿里云 V1 签名的原始串结构是:
VERB \n
Content-MD5 \n
Content-Type \n
Date \n
CanonicalizedOSSHeaders
CanonicalizedResource
写个十几行的脚本,把这段字符串打印出来,再手动 curl 一次带上相同的头,两边一比就知道差在哪。排查时按这个顺序看:Date 自己算的高频错,然后是资源路径的编码,最后才是 Content-Type 和 Content-MD5 是否和实际请求头一致。
注意:Content-Type 和 Content-MD5 也在签名串里。你签名时写了
text/plain,实际请求头却是application/octet-stream,一样 403,而且报的还是 SignatureDoesNotMatch。
小结
- 403 先看 XML 里的 Code,
SignatureDoesNotMatch才去查签名,RequestTimeTooSkewed直接对时,AccessDenied查权限。 - Date 必须 GMT 格式、UTC 时区、和请求头一致,客户端与服务器时间差不能超 15 分钟。
- 资源路径要和实际请求走的域名、Bucket 前缀、编码方式完全对应,多一个斜杠都不行。
- Query 里的子资源参数必须参与签名。
- 最快的定位办法:打印 StringToSign,和一次手动 curl 请求对照。
转载请注明出处,版权归原作者所有。
星耀SVIP
管理员
黑卡会员






