Стаття

Presigned URL в S3: що це, як працюють, ліміти терміну дії та приклади коду

Що таке presigned URL в S3, що означає кожен параметр X-Amz, ліміт у 7 днів, код для завантаження (CLI, boto3, JS) і як виправити типові помилки.

Presigned URL в S3 це тимчасове посилання на один об’єкт у приватному бакеті. Воно містить підпис у рядку запиту, тож той, хто його має, може скачати (або завантажити) цей об’єкт без облікових даних AWS, доки не мине термін дії посилання, а бакет ніколи не стає публічним. Ви створюєте його власними ключами, у своєму коді або з AWS CLI, і воно може діяти від 1 секунди до 7 днів. У цьому посібнику пояснено, як воно працює, що означає кожен параметр X-Amz-*, скільки воно може діяти, код для скачування й завантаження та помилки, з якими ви найімовірніше зіткнетеся. Ліміти взято з документації AWS і Cloudflare станом на жовтень 2026 року.

Presigned URL в S3 коротко

ЗапитанняВідповідь
Що він дозволяє?Одну операцію (GET, PUT, HEAD, DELETE…) з одним об’єктом
Максимальний термін дії7 днів (604 800 секунд) із SigV4 і ключами користувача IAM
Термін дії з тимчасовими ключамиЗакінчується разом із сесією ролі чи обліковими даними STS, залежно від того, що раніше
Діапазон у консолі S3Від 1 хвилини до 12 годин
Термін дії за замовчуванням3600 с в AWS CLI і boto3, 900 с у presigner JavaScript v3
Чи можна використати кілька разів?Так, скільки завгодно, доки не мине термін дії
Чи можна відкликати один URL?Ні, лише відкликавши облікові дані підпису або заборонивши його в політиці
Чи звертається створення до S3?Ні, підпис обчислюється локально

Як працює presigned URL

  1. Ваш код (або CLI) бере бакет, ключ об’єкта, метод HTTP і термін дії.
  2. SDK підписує ці значення вашим секретним ключем доступу за допомогою AWS Signature Version 4. На цьому етапі нічого не надсилається до S3.
  3. Ви передаєте отриманий URL комусь: браузеру, колезі, мобільному застосунку.
  4. Коли ним користуються, S3 заново обчислює підпис із секретом, що належить ключу доступу в URL. Якщо підпис збігається, термін дії URL не минув і той, хто підписав, має право на цю операцію, запит проходить.

URL несе дозволи того, хто його підписав. AWS каже про це прямо: якщо користувач IAM чи роль, що згенерували URL, не можуть читати об’єкт, URL повертає 403, хоч яким дійсним був би підпис.

Що означають параметри X-Amz

Presigned URL із SigV4 виглядає так (переноси рядків додано):

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Алгоритм підпису: AWS4-HMAC-SHA256 для SigV4
X-Amz-CredentialID ключа доступу плюс область: <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Є лише тоді, коли URL підписано тимчасовими обліковими даними (STS, ролі, EC2, ECS, SSO)

Змініть будь-яку підписану частину (ключ, метод, термін дії чи підписаний заголовок), і підпис перестане збігатися. Зверніть увагу й на те, що URL показує відкритим текстом: назву бакета, повний ключ об’єкта та ваш ID ключа доступу. Секретного ключа в URL ніколи немає.

Скільки діє presigned URL

Ліміт залежить від облікових даних, якими його підписано:

Чим підписаноМаксимальний термін дії
Ключі доступу користувача IAM (SigV4)7 днів, бо сам ключ підпису SigV4 дійсний 7 днів
Сесія ролі IAM (STS AssumeRole)До завершення сесії, за замовчуванням 1 година
Профіль інстансу EC2До ротації облікових даних ролі, щонайбільше близько 6 годин
Роль завдання ECSДо ротації облікових даних завдання, зазвичай кожні 1-6 годин
Будь-які відкликані чи видалені облікові даніПерестає працювати негайно, що б не вказував X-Amz-Expires
Консоль S3Ви вибираєте від 1 хвилини до 12 годин

Це пояснює найпоширеніший сюрприз: ви просите 7 днів з функції Lambda чи інстансу EC2, а посилання вмирає через годину чи кілька годин. URL живе не довше за облікові дані, що за ним стоять. Якщо вам потрібні довгі посилання, підписуйте окремим користувачем IAM, який має доступ на читання лише до цього бакета.

S3 перевіряє термін дії на початку запиту. Велике скачування, що почалося за секунду до кінця терміну, завершиться нормально, але повторна спроба після терміну завершиться помилкою AccessDenied з повідомленням «Request has expired». Запит на понад 604 800 секунд відхиляється одразу.

Як згенерувати presigned URL

AWS CLI

aws s3 presign s3://my-bucket/reports/q3.pdf --expires-in 3600

Команда підписує URL для скачування (GET). За замовчуванням 3600 секунд, максимум 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 у регіонах, які його ще приймають, і тоді непомітно відкидаються деякі параметри й заголовки, як-от 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, presigner JavaScript використовує 900 секунд (15 хвилин).

Presigned 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. Бакету потрібне правило CORS, яке дозволяє ваше джерело (origin) і метод PUT. Для завантажень multipart воно також має відкривати заголовок ETag, бо браузер мусить читати його з кожної частини.
  • Фіксований ключ. Генеруйте унікальний ключ на сервері (наприклад, із префіксом UUID), щоб відвідувач не міг перезаписати чужий файл.
  • Контрольні суми. Поточні AWS SDK за замовчуванням додають до завантажень контрольну суму CRC. Якщо presigned-завантаження до S3-сумісного провайдера завершуються помилками підпису чи контрольної суми, встановіть у клієнті requestChecksumCalculation: 'WHEN_REQUIRED'.

Presigned POST це окремий механізм для звичайних HTML-форм із політикою, яка може обмежувати розмір і тип вмісту. Не кожен S3-сумісний провайдер його підтримує (R2 не підтримує), тож presigned PUT більш переносний вибір.

Великі файли: presigned-завантаження multipart

Окремий PUT в S3 обмежений 5 ГБ, а AWS радить multipart приблизно від 100 МБ. Multipart ділить файл на до 10 000 частин, кожна від 5 МіБ до 5 ГіБ, крім останньої, і вони завантажуються незалежно, паралельно й з повторними спробами для кожної частини. Підпис працює для кожної частини окремо: сервер починає завантаження, підписує по одному URL UploadPart на частину, браузер надсилає кожен фрагмент і повертає його 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 }] },
  })
)

Додайте правило життєвого циклу, яке скасовує незавершені завантаження multipart через кілька днів, інакше покинуті частини й далі займатимуть оплачуване місце.

Presigned URL в R2, B2, Spaces і Wasabi

Провайдери, які реалізують API S3 із SigV4, приймають той самий код: ви змінюєте лише endpoint, регіон і ключі. Деталі різняться, тож перевіряйте документацію провайдера:

  • Cloudflare R2 підтримує presigned GET, HEAD, PUT і DELETE з терміном дії від 1 секунди до 7 днів. Він не підтримує presigned POST, а presigned URL працюють лише на домені API S3 (<ACCOUNT_ID>.r2.cloudflarestorage.com), не на власних доменах. Використовуйте region: 'auto'.
  • Backblaze B2 зазначає, що його S3-сумісний API підтримує presigned URL, згенеровані за допомогою AWS CLI або SDK.
  • DigitalOcean Spaces і Wasabi працюють з тими самими викликами SDK, спрямованими на їхні endpoint.

Ціни кожного провайдера дивіться в порівнянні S3, R2, Spaces, Wasabi і B2.

Типові помилки presigned URL і як їх виправити

ПомилкаЗвичайна причинаВиправлення
SignatureDoesNotMatchМетод, заголовки чи запит відрізняються від підписаних; годинник розсинхронізованоНадсилайте той самий Content-Type і заголовки; синхронізуйте годинник через NTP
AccessDenied «Request has expired»URL уже після X-Amz-Date + X-Amz-ExpiresЗгенеруйте новий URL; скоротіть проміжок між підписанням і використанням
URL спливає раніше, ніж просилиПідписано тимчасовими обліковими даними (роль, EC2, ECS, Lambda)Підписуйте обліковими даними, які живуть стільки, скільки має діяти посилання
ExpiredTokenСесія STS за X-Amz-Security-Token завершиласяОновіть облікові дані перед підписанням
403 AccessDenied на новому URLТой, хто підписує, не має s3:GetObject чи s3:PutObject, або політика забороняєНадайте дозвіл тому, хто підписує; перевірте політики бакета
AuthorizationQueryParametersErrorТермін дії понад 604 800 секунд або хибна область облікових данихТримайте X-Amz-Expires на рівні 7 днів чи менше; підписуйте в регіоні бакета
Браузер блокує через CORSУ бакеті немає правила CORS для вашого джерела й методуДодайте правило CORS із вашим джерелом, GET/PUT і відкритим ETag
AccessDenied з HeadersNotSignedRange підписано, але If-Range надіслано без підписуДодайте If-Range до підписаних заголовків

Корпоративні проксі, які переписують заголовки чи рядки запиту, теж ламають підписи. Якщо URL працює з однієї мережі й не працює з іншої, перевірте без проксі.

Найкращі практики безпеки

AWS називає presigned URL токенами на пред’явника й радить їх захищати. На практиці:

  • Тримайте їх короткими. Хвилини для завантажень і чутливих файлів, години для звичайних скачувань. Генеруйте новий, коли комусь знову потрібен файл.
  • Підписуйте вузькою ідентичністю. Окремий користувач IAM чи роль, що можуть лише читати (або лише записувати) потрібний префікс, обмежують шкоду, якщо посилання витече.
  • Додайте обмеження в політиці бакета. Умова s3:signatureAge відхиляє підписи, старші за заданий вік, а aws:SourceIp обмежує, з яких мереж ними можна користуватися.
  • Не записуйте їх у журнали. Повні presigned URL у журналах застосунку, аналітиці чи історії чатів це робочі облікові дані, доки не мине термін дії.
  • Плануйте з огляду на те, чого їм бракує. Немає пароля, лічильника завантажень і відкликання окремого посилання, тож поставте попереду застосунок, коли вам це потрібно.

Детальніше про спільний доступ з варіантами, які додають ці засоби контролю, читайте в статті як ділитися файлами S3, не роблячи бакет публічним.

Як Nubbo використовує presigned URL

Nubbo побудовано на цьому підході. Коли ви завантажуєте файли в провіднику, сервер підписує PUT (або, від 100 МБ, завантаження multipart із трьома частинами паралельно), і ваш браузер надсилає байти просто у ваш бакет. Скачування окремих файлів використовують presigned URL GET, дійсні одну годину. Ваші ключі доступу зберігаються зашифрованими за допомогою AES-256-GCM і використовуються лише на сервері для підпису.

Посилання для спільного доступу додають те, чого бракує звичайним presigned URL: необов’язкові пароль, термін дії та ліміт завантажень, які Nubbo перевіряє, перш ніж видати URL із коротким терміном дії, а посилання можна будь-коли деактивувати. Багатофайлові ZIP-завантаження збираються у вашому браузері, який скачує кожен файл просто з вашого бакета: Nubbo ніколи не бачить вмісту. Один чесний виняток: мініатюри записуються у ваш власний бакет у .nubbo-thumbs/. Усе працює однаково з Amazon S3, Cloudflare R2, DigitalOcean Spaces, Wasabi, Backblaze B2 і публічними S3-сумісними endpoint.

Поширені запитання

Що таке presigned URL в S3?

Це звичайне посилання HTTPS на один об’єкт у приватному бакеті з підписом, доданим до рядка запиту. Той, хто має посилання, може виконати одну операцію, для якої його підписано, зазвичай скачування (GET) або завантаження (PUT), доки не мине термін дії. Бакет залишається приватним, а людині, яка користується посиланням, не потрібні облікові дані AWS.

Який максимальний термін дії presigned URL в S3?

Сім днів (604 800 секунд), якщо ви підписуєте за допомогою Signature Version 4 і довгострокових ключів користувача IAM. 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 підписано тимчасовими обліковими даними.

Чи можна відкликати presigned URL в S3 до завершення терміну дії?

Не поодинці. Presigned URL перестає працювати достроково, лише коли облікові дані, якими його підписано, відкликано, видалено чи деактивовано, або коли політика бакета забороняє запит. Якщо вам потрібно скасовувати окремі посилання, поставте попереду застосунок, який видає URL із коротким терміном дії лише після власних перевірок.

Чи безпечні presigned URL в S3?

Підпис неможливо підробити чи змінити, а бакет залишається приватним. Проте AWS описує presigned URL як токени на пред’явника (bearer tokens): будь-хто, хто отримав посилання, може користуватися ним скільки завгодно разів, доки не мине термін дії. Тримайте терміни короткими, використовуйте HTTPS і не публікуйте посилання там, де їх можуть скопіювати.

Чи працюють presigned URL з Cloudflare R2, Backblaze B2 чи Wasabi?

Провайдери, які реалізують API S3 із Signature Version 4, зазвичай їх підтримують, і ви використовуєте ті самі виклики SDK з endpoint провайдера. Cloudflare R2, наприклад, підтримує presigned GET, HEAD, PUT і DELETE з терміном дії від 1 секунди до 7 днів, але не завантаження через форми POST і не власні домени.

Чому мій presigned URL повертає SignatureDoesNotMatch?

AWS називає три типові причини: розсинхронізований годинник комп’ютера, проксі, який змінює заголовки або рядок запиту, і запит, метод, заголовки чи параметри якого відрізняються від підписаних. Під час завантажень зазвичай винен інший Content-Type, ніж той, з яким генерували URL.

Джерела

Ціни та ліміти взято з офіційних сторінок нижче. Провайдери їх змінюють, тож перевірте, перш ніж ухвалювати рішення.