AWS预签名URL初始访问报403错误,等待15秒后恢复正常
我在后端用以下代码生成AWS S3存储桶中MP3文件的预签名URL:
const s3Client = new S3Client({ credentials: { accessKeyId: "AAAAAAAAAAAAAAAAAAAA", secretAccessKey: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } }); const getObjectCommand = new GetObjectCommand({ Bucket: "my-bucket-name", Key: "file-to-be-used" }); // 生成GET请求的预签名URL const url = await getSignedUrl(s3Client, getObjectCommand, { expiresIn: 300 }).then(data => { ... 做一些处理后返回URL ... });
Postman表现
调用后端接口获取预签名URL后,在Postman中可立即正常访问(直接点击即可打开)。
浏览器表现
但在浏览器中使用该URL时(比如设为HTMLAudioElement的源,或通过fetch请求访问),会出现403 - Access forbidden错误。
异常细节
等待约15秒后再访问同一个URL,就能正常使用了——直接点击可在浏览器打开,设为音频元素源也能正常播放。
操作过程显示:获取URL后,两次尝试访问(一次设为音频源、一次fetch请求)都返回403;初始两次在新标签页打开URL也报403,第三次点击(或等待15秒后)则恢复正常。
- 无任何CORS相关错误(已配置CORS,
AllowedHeaders和AllowedOrigins均设为*) - Postman和浏览器用的是同一个后端生成的完全相同的URL
更新
按建议改用us-east-1标准区域的存储桶,问题依旧。
Postman中的请求及请求头
请求URL:
https://presigned-url-test-tim.s3.us-east-1.amazonaws.com/4f65f31b-7d63-4be0-a289-1c3a8e056ea4.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIASAQ3L4XYIZI4YD73%2F20230311%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230311T220846Z&X-Amz-Expires=300&X-Amz-Signature=b3c081018ea7e9f7979a65267f66bfdcf2f185d2f824bd86fa9ae9141e8ba627&X-Amz-SignedHeaders=host&x-id=GetObject
拆分后:
https://presigned-url-test-tim.s3.us-east-1.amazonaws.com/4f65f31b-7d63-4be0-a289-1c3a8e056ea4.mp3? X-Amz-Algorithm=AWS4-HMAC-SHA256& X-Amz-Content-Sha256=UNSIGNED-PAYLOAD& X-Amz-Credential=AKIASAQ3L4XYIZI4YD73%2F20230311%2Fus-east-1%2Fs3%2Faws4_request& X-Amz-Date=20230311T220846Z& X-Amz-Expires=300& X-Amz-Signature=b3c081018ea7e9f7979a65267f66bfdcf2f185d2f824bd86fa9ae9141e8ba627& X-Amz-SignedHeaders=host& x-id=GetObject
请求头:
User-Agent: PostmanRuntime/7.31.1 Accept: */* Postman-Token: 4364aac9-1334-4434-b9a9-0b792dbde7b5 Host: presigned-url-test-tim.s3.us-east-1.amazonaws.com Accept-Encoding: gzip, deflate, br Connection: keep-alive
浏览器中的请求及请求头
请求URL:
https://presigned-url-test-tim.s3.us-east-1.amazonaws.com/4f65f31b-7d63-4be0-a289-1c3a8e056ea4.mp3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIASAQ3L4XYIZI4YD73/20230311/us-east-1/s3/aws4_request&X-Amz-Date=20230311T220557Z&X-Amz-Expires=300&X-Amz-Signature=e58bdbb1af3d4d47e1ec7c2aeaab3f77dd3a1fd7987831f1d0e6367a359637ef&X-Amz-SignedHeaders=host&x-id=GetObject
拆分后:
https://presigned-url-test-tim.s3.us-east-1.amazonaws.com/4f65f31b-7d63-4be0-a289-1c3a8e056ea4.mp3? X-Amz-Algorithm=AWS4-HMAC-SHA256& X-Amz-Content-Sha256=UNSIGNED-PAYLOAD& X-Amz-Credential=AKIASAQ3L4XYIZI4YD73/20230311/us-east-1/s3/aws4_request& X-Amz-Date=20230311T220557Z& X-Amz-Expires=300& X-Amz-Signature=e58bdbb1af3d4d47e1ec7c2aeaab3f77dd3a1fd7987831f1d0e6367a359637ef& X-Amz-SignedHeaders=host& x-id=GetObject
1. 后端与S3的时间同步偏差
AWS签名机制依赖精确的X-Amz-Date时间戳,若后端服务器时间与AWS服务器时间差超过5分钟,会直接导致签名验证失败。等待15秒后时间偏差缩小到允许范围内,URL就会生效。
解决方法:
- 开启后端服务器的NTP服务,同步标准UTC时间。
- 检查服务器时间与UTC的差值,确保控制在5分钟以内。
2. URL编码不一致问题
对比Postman和浏览器的请求URL,X-Amz-Credential参数中的斜杠在Postman中是URL编码后的%2F,但浏览器中是原始的/。虽然多数场景下浏览器会自动处理,但部分情况会触发签名验证失败。
解决方法:
- 后端生成预签名URL后,对特殊字符(如斜杠)做URL编码再返回给前端。
- 前端接收URL后,用
encodeURI()对整个URL编码后再使用。
3. S3最终一致性延迟
如果MP3文件是刚上传就生成预签名URL,S3的最终一致性模型会导致短时间内对象未完成同步,返回403。等待15秒后对象同步完成,即可正常访问。
解决方法:
- 若文件是刚上传的,延迟几秒再生成预签名URL。
- 开启S3版本控制,生成URL时携带
VersionId参数;或通过PUT操作返回的ETag确认对象写入完成后再生成URL。
4. 浏览器预加载机制干扰
浏览器可能会在URL返回后立即发起预请求(比如音频元素的自动预加载),此时签名因时间偏差等原因未生效,导致403。等待后重新请求时条件满足即可访问。
解决方法:
- 对HTMLAudioElement设置
preload="none",需要播放时再手动设置src并调用load()。 - 手动控制请求时机,避免浏览器自动预请求。
内容的提问来源于stack exchange,提问作者Gorgsenegger

