文章

S3 預先簽署 URL:是什麼、如何運作、有效期限與程式碼範例

什麼是 S3 預先簽署 URL、每個 X-Amz 參數的意義、7 天有效期限上限、上傳與下載程式碼(CLI、boto3、JS),以及常見錯誤的解決方法。

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 如何運作

  1. 你的程式碼(或 CLI)取得儲存桶、物件鍵、HTTP 方法和有效期限。
  2. SDK 使用 AWS Signature Version 4,以你的私密存取金鑰簽署這些值。此時不會傳送任何東西到 S3。
  3. 你把產生的 URL 交給某人:瀏覽器、同事、行動應用程式。
  4. 當對方使用它時,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-DateURL 的簽署時間,UTC,yyyyMMddTHHmmssZ
X-Amz-ExpiresURL 在 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)使用壽命和連結所需期限一樣長的憑證簽署
ExpiredTokenX-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 附帶 HeadersNotSignedRange 已簽署,但傳送了未簽署的 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 時使用的不同。

資料來源

價格與限制取自以下官方頁面。服務供應商會調整這些內容,決定前請先確認。