S3 預先簽署 URL 是指向私人儲存桶中某個物件的臨時連結。它的查詢字串中帶有簽章,所以持有者在連結到期前不需要 AWS 憑證就能下載(或上傳)該物件,而儲存桶永遠不會變成公開。你用自己的金鑰在程式碼中或透過 AWS CLI 建立它,有效期限從 1 秒到 7 天。本指南說明它如何運作、每個 X-Amz-* 參數的意義、能維持多久、下載和上傳的程式碼,以及你最可能遇到的錯誤。限制取自截至 2026 年 10 月的 AWS 和 Cloudflare 文件。
S3 預先簽署 URL 一覽
| 問題 | 答案 |
|---|---|
| 它授予什麼? | 對一個物件的一項操作(GET、PUT、HEAD、DELETE…) |
| 最長有效期限 | 使用 SigV4 和 IAM 使用者金鑰時為 7 天(604,800 秒) |
| 使用臨時金鑰時的期限 | 在角色工作階段或 STS 憑證結束時結束,以先發生者為準 |
| S3 主控台的範圍 | 1 分鐘到 12 小時 |
| 預設有效期限 | AWS CLI 和 boto3 為 3,600 秒,JavaScript v3 presigner 為 900 秒 |
| 可以使用多次嗎? | 可以,在到期前任何人都能無限次使用 |
| 可以撤銷單一 URL 嗎? | 不行,只能撤銷簽署憑證或在政策中拒絕 |
| 建立時會呼叫 S3 嗎? | 不會,簽章是在本機計算的 |
預先簽署 URL 如何運作
- 你的程式碼(或 CLI)取得儲存桶、物件鍵、HTTP 方法和有效期限。
- SDK 使用 AWS Signature Version 4,以你的私密存取金鑰簽署這些值。此時不會傳送任何東西到 S3。
- 你把產生的 URL 交給某人:瀏覽器、同事、行動應用程式。
- 當對方使用它時,S3 會用 URL 中存取金鑰所對應的私密金鑰重新計算簽章。如果簽章相符、URL 未過期,且簽署者有權執行該操作,請求就會通過。
URL 帶有簽署者的權限。AWS 對此說得很明確:如果產生 URL 的 IAM 使用者或角色無法讀取該物件,無論簽章多麼有效,URL 都會回傳 403。
X-Amz 參數的意義
SigV4 預先簽署 URL 看起來像這樣(已加入換行):
https://my-bucket.s3.eu-west-1.amazonaws.com/reports/q3.pdf
?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIAIOSFODNN7EXAMPLE%2F20261017%2Feu-west-1%2Fs3%2Faws4_request
&X-Amz-Date=20261017T093000Z
&X-Amz-Expires=3600
&X-Amz-SignedHeaders=host
&X-Amz-Signature=5f2c...e91a
| 參數 | 意義 |
|---|---|
X-Amz-Algorithm | 簽署演算法:SigV4 為 AWS4-HMAC-SHA256 |
X-Amz-Credential | 存取金鑰 ID 加上範圍:<key-id>/<YYYYMMDD>/<region>/s3/aws4_request |
X-Amz-Date | URL 的簽署時間,UTC,yyyyMMddTHHmmssZ |
X-Amz-Expires | URL 在 X-Amz-Date 之後的有效秒數,從 1 到 604800 |
X-Amz-SignedHeaders | 簽章涵蓋的標頭,至少一定有 host |
X-Amz-Signature | 簽章,64 個十六進位字元 |
X-Amz-Security-Token | 只在以臨時憑證(STS、角色、EC2、ECS、SSO)簽署 URL 時出現 |
變更任何已簽署的部分(物件鍵、方法、有效期限或已簽署的標頭),簽章就不再相符。也請注意 URL 以明文透露了哪些資訊:儲存桶名稱、完整的物件鍵和你的存取金鑰 ID。私密金鑰永遠不會出現在 URL 中。
預先簽署 URL 能維持多久
上限取決於簽署它的憑證:
| 簽署方式 | 最長有效期限 |
|---|---|
| IAM 使用者存取金鑰(SigV4) | 7 天,因為 SigV4 簽署金鑰本身的有效期為 7 天 |
| IAM 角色工作階段(STS AssumeRole) | 直到工作階段結束,預設 1 小時 |
| EC2 執行個體設定檔 | 直到角色憑證輪替,最多約 6 小時 |
| ECS 任務角色 | 直到任務憑證輪替,通常每 1 到 6 小時 |
| 任何已撤銷或刪除的憑證 | 立即失效,無論 X-Amz-Expires 是多少 |
| S3 主控台 | 你可以選擇 1 分鐘到 12 小時 |
這解釋了最常見的意外:你從 Lambda 函式或 EC2 執行個體要求 7 天,但連結在一小時或幾小時後就失效了。URL 的壽命只能和背後的憑證一樣長。如果你需要長效連結,請用一個只能讀取該儲存桶的專用 IAM 使用者來簽署。
S3 會在請求開始時檢查有效期限。在截止前一秒開始的大型下載會正常完成,但截止後的重試會以 AccessDenied 和「Request has expired」訊息失敗。要求超過 604,800 秒會被直接拒絕。
如何產生預先簽署 URL
AWS CLI
aws s3 presign s3://my-bucket/reports/q3.pdf --expires-in 3600
這個命令會簽署一個下載(GET)URL。預設為 3,600 秒,最長 604,800 秒。較新的 CLI 版本一律使用 SigV4,所以請確認已設定區域。上傳 URL 則需要使用 SDK。
Python(boto3)
import boto3
from botocore.config import Config
s3 = boto3.client('s3', region_name='eu-west-1', config=Config(signature_version='s3v4'))
url = s3.generate_presigned_url(
ClientMethod='get_object',
Params={
'Bucket': 'my-bucket',
'Key': 'reports/q3.pdf',
'ResponseContentDisposition': 'attachment; filename="q3.pdf"',
},
ExpiresIn=3600,
)
請明確設定 signature_version='s3v4'。boto3 參考文件警告,為了向後相容,否則它可能在仍接受舊版 Signature Version 2 的區域使用 SigV2 簽署,這會悄悄捨棄部分參數和標頭,例如 Range。ResponseContentDisposition 是選用的:它讓瀏覽器以乾淨的檔名儲存檔案,而不是直接在頁面中開啟。
JavaScript(AWS SDK v3)
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const client = new S3Client({ region: 'eu-west-1' })
const url = await getSignedUrl(
client,
new GetObjectCommand({ Bucket: 'my-bucket', Key: 'reports/q3.pdf' }),
{ expiresIn: 3600 }
)
如果省略 expiresIn,JavaScript presigner 會使用 900 秒(15 分鐘)。
預先簽署上傳 URL(PUT)
同樣的概念也能反向運作。你為一個物件鍵簽署 PutObject,持有 URL 的人就能寫入該物件。檔案直接從對方的裝置傳到儲存桶,你的伺服器完全不會經手這些位元組。如果已經存在相同物件鍵的物件,它會被取代。
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const client = new S3Client({ region: 'eu-west-1' })
const url = await getSignedUrl(
client,
new PutObjectCommand({
Bucket: 'my-bucket',
Key: 'incoming/brief.pdf',
ContentType: 'application/pdf',
}),
{ expiresIn: 900, signableHeaders: new Set(['content-type']) }
)
await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/pdf' },
body: file,
})
把 content-type 加入 signableHeaders 會將 URL 綁定到該類型,所以使用不同 Content-Type 的上傳會被拒絕。在 boto3 中對應的寫法是 generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}),用戶端必須傳送相同的標頭。
從瀏覽器上傳前要檢查三件事:
- CORS。儲存桶需要一條允許你的來源和
PUT方法的 CORS 規則。若使用分段上傳,還必須公開ETag標頭,因為瀏覽器必須從每個分段讀取它。 - 固定的物件鍵。在伺服器上產生唯一的物件鍵(例如加上 UUID 前綴),讓訪客無法覆寫別人的檔案。
- 總和檢查碼。目前的 AWS SDK 預設會為上傳加上 CRC 總和檢查碼。如果對 S3 相容服務供應商的預先簽署上傳因簽章或總和檢查碼錯誤而失敗,請在用戶端設定
requestChecksumCalculation: 'WHEN_REQUIRED'。
預先簽署 POST 是另一種機制,適用於一般 HTML 表單,可透過政策限制大小和內容類型。並非所有 S3 相容服務供應商都支援它(R2 就不支援),所以預先簽署 PUT 是可攜性較高的選擇。
大型檔案:預先簽署分段上傳
對 S3 的單次 PUT 上限為 5 GB,AWS 建議從約 100 MB 起使用分段上傳。分段上傳會把檔案切成最多 10,000 個分段,除了最後一段之外,每段 5 MiB 到 5 GiB,各分段獨立、平行上傳,並可個別重試。預先簽署是以分段為單位:伺服器啟動上傳,為每個分段簽署一個 UploadPart URL,瀏覽器傳送每個區塊並回報其 ETag,最後由伺服器完成上傳。
import {
CreateMultipartUploadCommand,
UploadPartCommand,
CompleteMultipartUploadCommand,
} from '@aws-sdk/client-s3'
const { UploadId } = await client.send(
new CreateMultipartUploadCommand({ Bucket: 'my-bucket', Key: 'video/raw.mov' })
)
const partUrl = await getSignedUrl(
client,
new UploadPartCommand({ Bucket: 'my-bucket', Key: 'video/raw.mov', UploadId, PartNumber: 1 }),
{ expiresIn: 3600 }
)
await client.send(
new CompleteMultipartUploadCommand({
Bucket: 'my-bucket',
Key: 'video/raw.mov',
UploadId,
MultipartUpload: { Parts: [{ PartNumber: 1, ETag: etagFromPartResponse }] },
})
)
請新增一條生命週期規則,在幾天後中止未完成的分段上傳,否則被遺棄的分段會持續佔用計費的儲存空間。
R2、B2、Spaces 和 Wasabi 上的預先簽署 URL
以 SigV4 實作 S3 API 的服務供應商接受相同的程式碼:你只需要變更端點、區域和金鑰。細節各有不同,請查看服務供應商的文件:
- Cloudflare R2 支援預先簽署的
GET、HEAD、PUT和DELETE,有效期限從 1 秒到 7 天。它不支援預先簽署POST,而且預先簽署 URL 只能在 S3 API 網域(<ACCOUNT_ID>.r2.cloudflarestorage.com)上使用,無法用於自訂網域。請使用region: 'auto'。 - Backblaze B2 表示,其 S3 相容 API 支援以 AWS CLI 或 SDK 產生的預先簽署 URL。
- DigitalOcean Spaces 和 Wasabi 只要將相同的 SDK 呼叫指向其端點即可使用。
各服務供應商的價格,請見 S3、R2、Spaces、Wasabi 與 B2 比較。
常見的預先簽署 URL 錯誤及解決方法
| 錯誤 | 常見原因 | 解決方法 |
|---|---|---|
SignatureDoesNotMatch | 方法、標頭或查詢與簽署時不同;時鐘不同步 | 傳送相同的 Content-Type 和標頭;用 NTP 同步時鐘 |
AccessDenied「Request has expired」 | URL 已超過 X-Amz-Date + X-Amz-Expires | 產生新的 URL;縮短簽署和使用之間的間隔 |
| URL 比要求的更早到期 | 以臨時憑證簽署(角色、EC2、ECS、Lambda) | 使用壽命和連結所需期限一樣長的憑證簽署 |
ExpiredToken | X-Amz-Security-Token 背後的 STS 工作階段已結束 | 簽署前先重新整理憑證 |
新 URL 出現 403 AccessDenied | 簽署者缺少 s3:GetObject 或 s3:PutObject,或政策拒絕了請求 | 將權限授予簽署主體;檢查儲存桶政策 |
AuthorizationQueryParametersError | 有效期限超過 604,800 秒,或憑證範圍格式錯誤 | 讓 X-Amz-Expires 保持在 7 天以內;在儲存桶所在區域簽署 |
| 瀏覽器被 CORS 封鎖 | 儲存桶沒有針對你的來源和方法的 CORS 規則 | 新增包含你的來源、GET/PUT 和公開 ETag 的 CORS 規則 |
AccessDenied 附帶 HeadersNotSigned | Range 已簽署,但傳送了未簽署的 If-Range | 將 If-Range 加入已簽署的標頭 |
會改寫標頭或查詢字串的企業代理伺服器也會破壞簽章。如果 URL 在某個網路可用、在另一個網路不可用,請在不經過代理伺服器的情況下測試。
安全性最佳做法
AWS 稱預先簽署 URL 為持有人權杖,並建議加以保護。實務上:
- 保持短效。上傳和敏感檔案以分鐘計,一般下載以小時計。有人再次需要檔案時,就產生新的 URL。
- 以權限範圍窄的身分簽署。只能讀取(或只能寫入)相關前綴的專用 IAM 使用者或角色,可以在連結外洩時限縮損害。
- 在儲存桶政策中加上防護。
s3:signatureAge條件會拒絕超過指定時間的簽章,aws:SourceIp則限制哪些網路可以使用它們。 - 不要記錄它們。應用程式日誌、分析或聊天紀錄中的完整預先簽署 URL,在到期前都是可用的憑證。
- 規劃它們缺少的功能。它們沒有密碼、沒有下載次數,也無法逐一撤銷,所以需要這些功能時,請在前面放一個應用程式。
關於分享方面的深入說明,以及能加上這些控制的選項,請見 如何在不公開儲存桶的情況下分享 S3 檔案。
Nubbo 如何使用預先簽署 URL
Nubbo 正是建立在這個模式之上。當你在檔案總管中上傳時,伺服器會簽署一個 PUT(100 MB 以上則是三個分段平行的分段上傳),你的瀏覽器會直接把位元組傳送到你的儲存桶。單一檔案下載使用有效期一小時的預先簽署 GET URL。你的存取金鑰以 AES-256-GCM 加密儲存,只在伺服器上用於簽署。
分享連結 補上了原始預先簽署 URL 缺少的功能:可選的密碼、到期日和下載次數限制,Nubbo 會在發出短期 URL 前先檢查這些條件,你也可以隨時停用連結。多檔案 ZIP 下載會在你的瀏覽器中打包,瀏覽器直接從你的儲存桶下載每個檔案:Nubbo 從不接觸檔案內容。只有一個坦白說明的例外:縮圖會寫入你自己儲存桶中的 .nubbo-thumbs/。它在 Amazon S3、Cloudflare R2、DigitalOcean Spaces、Wasabi、Backblaze B2 和公開的 S3 相容端點上的運作方式都相同。
常見問題
S3 中的預先簽署 URL 是什麼?
它是指向私人儲存桶中某個物件的一般 HTTPS 連結,查詢字串中附加了簽章。持有連結的人可以在到期前執行它所簽署的那一項操作,通常是下載(GET)或上傳(PUT)。儲存桶保持私人,使用連結的人也不需要 AWS 憑證。
S3 預先簽署 URL 的最長有效期限是多久?
以 Signature Version 4 和長期 IAM 使用者金鑰簽署時為七天(604,800 秒)。以臨時憑證簽署的 URL(例如擔任的角色或 EC2 執行個體設定檔),會在這些憑證到期時失效,即使你要求的期限更長。在 S3 主控台中,你可以選擇 1 分鐘到 12 小時。
X-Amz-Algorithm、X-Amz-Credential、X-Amz-Date、X-Amz-Expires、X-Amz-SignedHeaders 和 X-Amz-Signature 是什麼意思?
它們是 SigV4 的驗證參數。Algorithm 是簽署方法(AWS4-HMAC-SHA256),Credential 是你的存取金鑰 ID 加上日期、區域和服務範圍,Date 是 URL 的簽署時間,Expires 是有效的秒數,SignedHeaders 列出簽章涵蓋的標頭,Signature 則是簽章本身。X-Amz-Security-Token 只會在以臨時憑證簽署 URL 時出現。
我可以在 S3 預先簽署 URL 到期前撤銷它嗎?
無法逐一撤銷。只有在簽署它的憑證被撤銷、刪除或停用,或儲存桶政策拒絕該請求時,預先簽署 URL 才會提前失效。如果你需要取消個別連結,請在前面放一個應用程式,在完成自己的檢查後才發出短期有效的 URL。
S3 預先簽署 URL 安全嗎?
簽章無法偽造或竄改,儲存桶也保持私人。但 AWS 將預先簽署 URL 描述為持有人權杖:任何取得連結的人都能使用,而且在到期前可以無限次使用。請保持較短的有效期限、使用 HTTPS,並避免在可能被複製的地方張貼連結。
預先簽署 URL 可以用於 Cloudflare R2、Backblaze B2 或 Wasabi 嗎?
以 Signature Version 4 實作 S3 API 的服務供應商一般都支援,你只要搭配服務供應商的端點使用相同的 SDK 呼叫即可。例如 Cloudflare R2 支援預先簽署的 GET、HEAD、PUT 和 DELETE,有效期限為 1 秒到 7 天,但不支援 POST 表單上傳,也不支援自訂網域。
為什麼我的預先簽署 URL 回傳 SignatureDoesNotMatch?
AWS 列出三個常見原因:電腦時鐘不同步、代理伺服器變更了標頭或查詢字串,以及請求的方法、標頭或參數與簽署時不同。上傳時最常見的原因是傳送的 Content-Type 與產生 URL 時使用的不同。
資料來源
價格與限制取自以下官方頁面。服務供應商會調整這些內容,決定前請先確認。