Presigned URL w S3, czyli podpisany adres URL, to tymczasowy link do jednego obiektu w prywatnym buckecie. W query stringu ma podpis, więc każdy, kto go posiada, może pobrać (lub przesłać) ten obiekt bez danych uwierzytelniających AWS, dopóki link nie wygaśnie, a bucket nigdy nie staje się publiczny. Tworzysz go własnymi kluczami, w kodzie lub z AWS CLI, i może być ważny od 1 sekundy do 7 dni. Ten poradnik wyjaśnia, jak to działa, co oznacza każdy parametr X-Amz-*, jak długo link może być ważny, pokazuje kod do pobierania i przesyłania oraz błędy, na które najczęściej się trafia. Limity pochodzą z dokumentacji AWS i Cloudflare według stanu na październik 2026.
Presigned URL w S3 w skrócie
| Pytanie | Odpowiedź |
|---|---|
| Na co pozwala? | Jedna operacja (GET, PUT, HEAD, DELETE…) na jednym obiekcie |
| Maksymalna ważność | 7 dni (604 800 sekund) z SigV4 i kluczami użytkownika IAM |
| Ważność z kluczami tymczasowymi | Kończy się wraz z sesją roli lub danymi STS, zależnie od tego, co nastąpi pierwsze |
| Zakres w konsoli S3 | Od 1 minuty do 12 godzin |
| Domyślna ważność | 3600 s w AWS CLI i boto3, 900 s w presignerze JavaScript v3 |
| Czy można go użyć kilka razy? | Tak, dowolnie często aż do wygaśnięcia |
| Czy można unieważnić jeden URL? | Nie, tylko przez unieważnienie danych, którymi podpisano, lub odmowę w polityce |
| Czy utworzenie go wywołuje S3? | Nie, podpis jest obliczany lokalnie |
Jak działa presigned URL
- Twój kod (lub CLI) przyjmuje bucket, klucz obiektu, metodę HTTP i czas ważności.
- SDK podpisuje te wartości twoim secret access key za pomocą AWS Signature Version 4. Na tym etapie nic nie jest wysyłane do S3.
- Przekazujesz wynikowy URL komuś: przeglądarce, współpracownikowi, aplikacji mobilnej.
- Gdy ktoś go użyje, S3 ponownie oblicza podpis za pomocą secret należącego do klucza dostępu zawartego w URL. Jeśli podpis się zgadza, URL nie wygasł, a podpisujący ma uprawnienia do tej operacji, żądanie zostaje wykonane.
URL niesie uprawnienia tego, kto go podpisał. AWS mówi o tym wprost: jeśli użytkownik lub rola IAM, która wygenerowała URL, nie może odczytać obiektu, URL zwraca 403 bez względu na to, jak poprawny jest podpis.
Co oznaczają parametry X-Amz
Presigned URL w SigV4 wygląda tak (z dodanymi podziałami wierszy):
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
| Parametr | Znaczenie |
|---|---|
X-Amz-Algorithm | Algorytm podpisu: AWS4-HMAC-SHA256 dla SigV4 |
X-Amz-Credential | Identyfikator klucza dostępu wraz z zakresem: <key-id>/<YYYYMMDD>/<region>/s3/aws4_request |
X-Amz-Date | Moment podpisania URL, w UTC, yyyyMMddTHHmmssZ |
X-Amz-Expires | Liczba sekund ważności URL od X-Amz-Date, od 1 do 604800 |
X-Amz-SignedHeaders | Nagłówki objęte podpisem, zawsze co najmniej host |
X-Amz-Signature | Podpis, 64 znaki szesnastkowe |
X-Amz-Security-Token | Obecny tylko wtedy, gdy URL podpisano tymczasowymi danymi uwierzytelniającymi (STS, role, EC2, ECS, SSO) |
Zmień dowolną podpisaną część (klucz, metodę, ważność lub podpisany nagłówek), a podpis przestanie się zgadzać. Zwróć też uwagę, co URL ujawnia otwartym tekstem: nazwę bucketu, pełny klucz obiektu i identyfikator twojego klucza dostępu. Klucz tajny nigdy nie trafia do URL.
Jak długo ważny jest presigned URL
Limit zależy od danych uwierzytelniających, którymi go podpisano:
| Podpisany za pomocą | Maksymalna ważność |
|---|---|
| Kluczy dostępu użytkownika IAM (SigV4) | 7 dni, bo sam klucz podpisu SigV4 jest ważny przez 7 dni |
| Sesji roli IAM (STS AssumeRole) | Do końca sesji, domyślnie 1 godzina |
| Profilu instancji EC2 | Do rotacji danych roli, najwyżej około 6 godzin |
| Roli zadania ECS | Do rotacji danych zadania, zwykle co 1 do 6 godzin |
| Dowolnych danych unieważnionych lub usuniętych | Przestaje działać natychmiast, bez względu na X-Amz-Expires |
| Konsoli S3 | Wybierasz od 1 minuty do 12 godzin |
To wyjaśnia najczęstsze zaskoczenie: prosisz o 7 dni z funkcji Lambda lub instancji EC2, a link przestaje działać po godzinie lub kilku godzinach. URL żyje tylko tak długo jak dane uwierzytelniające, które za nim stoją. Jeśli potrzebujesz długich linków, podpisuj je osobnym użytkownikiem IAM z dostępem do odczytu tylko tego bucketu.
S3 sprawdza ważność w chwili rozpoczęcia żądania. Duże pobieranie, które zaczyna się sekundę przed terminem, kończy się normalnie, ale ponowna próba po terminie kończy się błędem AccessDenied z komunikatem „Request has expired”. Prośba o więcej niż 604 800 sekund jest od razu odrzucana.
Jak wygenerować presigned URL
AWS CLI
aws s3 presign s3://my-bucket/reports/q3.pdf --expires-in 3600
Polecenie podpisuje URL do pobierania (GET). Domyślnie to 3600 sekund, a maksymalnie 604 800. Nowsze wersje CLI zawsze używają SigV4, więc upewnij się, że masz skonfigurowany region. Do URL przesyłania potrzebujesz 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,
)
Ustaw jawnie signature_version='s3v4'. Dokumentacja boto3 ostrzega, że ze względu na zgodność wsteczną w przeciwnym razie może podpisywać starszą Signature Version 2 w regionach, które ją jeszcze akceptują, co po cichu pomija niektóre parametry i nagłówki, takie jak Range. ResponseContentDisposition jest opcjonalny: sprawia, że przeglądarka zapisuje plik pod czystą nazwą, zamiast otwierać go w oknie.
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 }
)
Jeśli pominiesz expiresIn, presigner JavaScript użyje 900 sekund (15 minut).
Presigned URL do przesyłania (PUT)
Ten sam pomysł działa w drugą stronę. Podpisujesz PutObject dla jednego klucza, a każdy, kto ma URL, może zapisać ten obiekt. Plik trafia prosto z jego urządzenia do bucketu, a twój serwer nigdy nie obsługuje tych bajtów. Jeśli obiekt o tym kluczu już istnieje, zostaje zastąpiony.
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,
})
Dodanie content-type do signableHeaders wiąże URL z tym typem, więc przesłanie z innym Content-Type zostanie odrzucone. W boto3 odpowiednikiem jest generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}), a przesyłający musi wysłać ten sam nagłówek.
Trzy rzeczy do sprawdzenia przed przesyłaniem z przeglądarki:
- CORS. Bucket potrzebuje reguły CORS, która dopuszcza twój origin i metodę
PUT. Przy przesyłaniu multipart musi też udostępniać nagłówekETag, bo przeglądarka musi go odczytać z każdej części. - Stały klucz. Generuj unikalny klucz na serwerze (na przykład z prefiksem UUID), aby odwiedzający nie mógł nadpisać cudzego pliku.
- Sumy kontrolne. Aktualne SDK AWS domyślnie dodają do przesyłanych plików sumę kontrolną CRC. Jeśli przesyłanie przez presigned URL do dostawcy zgodnego z S3 kończy się błędami podpisu lub sumy kontrolnej, ustaw w kliencie SDK
requestChecksumCalculation: 'WHEN_REQUIRED'.
Presigned POST to osobny mechanizm dla zwykłych formularzy HTML, z polityką, która może ograniczać rozmiar i typ zawartości. Nie każdy dostawca zgodny z S3 go obsługuje (R2 nie), więc presigned PUT jest wyborem bardziej przenośnym.
Duże pliki: przesyłanie multipart przez presigned URL
Pojedynczy PUT do S3 jest ograniczony do 5 GB, a AWS zaleca multipart od około 100 MB. Multipart dzieli plik na maksymalnie 10 000 części, każdą od 5 MiB do 5 GiB z wyjątkiem ostatniej, które są przesyłane niezależnie, równolegle i z ponawianiem dla każdej części. Podpisywanie działa dla każdej części osobno: serwer rozpoczyna przesyłanie, podpisuje po jednym URL UploadPart na część, przeglądarka wysyła każdy fragment i odsyła jego ETag, a serwer kończy przesyłanie.
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 }] },
})
)
Dodaj regułę cyklu życia, która przerywa niedokończone przesyłania multipart po kilku dniach, w przeciwnym razie porzucone części nadal zajmują płatną pamięć.
Presigned URL w R2, B2, Spaces i Wasabi
Dostawcy implementujący S3 API z SigV4 akceptują ten sam kod: zmieniasz tylko endpoint, region i klucze. Szczegóły się różnią, więc sprawdź dokumentację dostawcy:
- Cloudflare R2 obsługuje podpisane
GET,HEAD,PUTiDELETEz ważnością od 1 sekundy do 7 dni. Nie obsługuje podpisanegoPOST, a presigned URL działają tylko w domenie S3 API (<ACCOUNT_ID>.r2.cloudflarestorage.com), a nie we własnych domenach. Użyjregion: 'auto'. - Backblaze B2 podaje, że jego API zgodne z S3 obsługuje presigned URL generowane za pomocą AWS CLI lub SDK.
- DigitalOcean Spaces i Wasabi działają z tymi samymi wywołaniami SDK skierowanymi na ich endpointy.
Ceny u każdego dostawcy znajdziesz w porównaniu S3, R2, Spaces, Wasabi i B2.
Typowe błędy presigned URL i jak je naprawić
| Błąd | Zwykła przyczyna | Rozwiązanie |
|---|---|---|
SignatureDoesNotMatch | Metoda, nagłówki lub query różnią się od podpisanych; niezsynchronizowany zegar | Wysyłaj ten sam Content-Type i nagłówki; zsynchronizuj zegar przez NTP |
AccessDenied „Request has expired” | URL przekroczył X-Amz-Date + X-Amz-Expires | Wygeneruj nowy URL; skróć czas między podpisaniem a użyciem |
| URL wygasa wcześniej, niż żądano | Podpisany tymczasowymi danymi (rola, EC2, ECS, Lambda) | Podpisuj danymi, które żyją tak długo, jak ma działać link |
ExpiredToken | Sesja STS za X-Amz-Security-Token się skończyła | Odśwież dane uwierzytelniające przed podpisaniem |
403 AccessDenied przy świeżym URL | Podpisujący nie ma s3:GetObject lub s3:PutObject albo polityka tego zabrania | Nadaj uprawnienie podmiotowi, który podpisuje; sprawdź polityki bucketu |
AuthorizationQueryParametersError | Ważność powyżej 604 800 sekund lub błędny zakres danych uwierzytelniających | Trzymaj X-Amz-Expires na 7 dniach lub mniej; podpisuj w regionie bucketu |
| Przeglądarka zablokowana przez CORS | Brak w buckecie reguły CORS dla twojego originu i metody | Dodaj regułę CORS z twoim originem, GET/PUT i udostępnionym ETag |
AccessDenied z HeadersNotSigned | Range został podpisany, ale If-Range wysłano bez podpisu | Dodaj If-Range do podpisanych nagłówków |
Firmowe proxy, które przepisują nagłówki lub query stringi, również psują podpisy. Jeśli URL działa w jednej sieci, a w innej nie, przetestuj go bez proxy.
Dobre praktyki bezpieczeństwa
AWS nazywa presigned URL tokenami na okaziciela i zaleca ich ochronę. W praktyce:
- Ustawiaj krótką ważność. Minuty dla przesyłania i wrażliwych plików, godziny dla zwykłych pobrań. Generuj nowy, gdy ktoś znów potrzebuje pliku.
- Podpisuj tożsamością o wąskich uprawnieniach. Osobny użytkownik lub rola IAM, która może tylko odczytywać (lub tylko zapisywać) dany prefiks, ogranicza szkody, jeśli link wycieknie.
- Dodaj zabezpieczenia w polityce bucketu. Warunek
s3:signatureAgeodrzuca podpisy starsze niż ustalony wiek, aaws:SourceIpogranicza, z jakich sieci można ich używać. - Nie zapisuj ich w logach. Pełne presigned URL w logach aplikacji, analityce lub historii czatu są działającymi danymi dostępowymi aż do wygaśnięcia.
- Zaplanuj to, czego im brakuje. Nie mają hasła, licznika pobrań ani unieważniania pojedynczych linków, więc gdy tego potrzebujesz, postaw przed bucketem aplikację.
Więcej o udostępnianiu, z opcjami, które dodają te kontrole, przeczytasz w artykule jak udostępniać pliki S3 bez upubliczniania bucketu.
Jak Nubbo korzysta z presigned URL
Nubbo jest zbudowane na tym wzorcu. Gdy przesyłasz plik w eksploratorze, serwer podpisuje PUT (lub, od 100 MB, przesyłanie multipart z trzema częściami równolegle), a twoja przeglądarka wysyła bajty bezpośrednio do twojego bucketu. Pobieranie pojedynczych plików korzysta z podpisanych URL GET ważnych przez godzinę. Twoje klucze dostępu są przechowywane w postaci zaszyfrowanej algorytmem AES-256-GCM i są używane wyłącznie na serwerze do podpisywania.
Linki udostępniające dodają to, czego brakuje surowym presigned URL: opcjonalne hasło, datę wygaśnięcia i limit pobrań, które Nubbo sprawdza przed wydaniem krótkotrwałego URL, a link możesz w każdej chwili dezaktywować. Pobrania ZIP z wieloma plikami są składane w twojej przeglądarce, która pobiera każdy plik prosto z twojego bucketu: Nubbo nigdy nie widzi zawartości. Jeden uczciwy wyjątek: miniatury są zapisywane w twoim własnym buckecie w .nubbo-thumbs/. Wszystko działa tak samo w Amazon S3, Cloudflare R2, DigitalOcean Spaces, Wasabi, Backblaze B2 i publicznych endpointach zgodnych z S3.
Najczęściej zadawane pytania
Czym jest presigned URL w S3?
To zwykły link HTTPS do jednego obiektu w prywatnym buckecie z podpisem dodanym do query stringu. Każdy, kto ma link, może wykonać jedną operację, dla której został podpisany, zwykle pobranie (GET) lub przesłanie (PUT), aż do jego wygaśnięcia. Bucket pozostaje prywatny, a osoba korzystająca z linku nie potrzebuje danych uwierzytelniających AWS.
Jaka jest maksymalna ważność presigned URL w S3?
Siedem dni (604 800 sekund), gdy podpisujesz za pomocą Signature Version 4 i długoterminowych kluczy użytkownika IAM. URL podpisany tymczasowymi danymi uwierzytelniającymi, na przykład przejętą rolą lub profilem instancji EC2, przestaje działać, gdy te dane wygasną, nawet jeśli poprosisz o dłuższy czas. W konsoli S3 możesz wybrać od 1 minuty do 12 godzin.
Co oznaczają X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders i X-Amz-Signature?
To parametry uwierzytelniania SigV4. Algorithm to metoda podpisu (AWS4-HMAC-SHA256), Credential to identyfikator klucza dostępu wraz z zakresem daty, regionu i usługi, Date to moment podpisania URL, Expires to liczba sekund ważności, SignedHeaders wymienia nagłówki objęte podpisem, a Signature to sam podpis. X-Amz-Security-Token pojawia się tylko wtedy, gdy URL został podpisany tymczasowymi danymi uwierzytelniającymi.
Czy mogę unieważnić presigned URL w S3 przed jego wygaśnięciem?
Nie pojedynczo. Presigned URL przestaje działać wcześniej tylko wtedy, gdy dane uwierzytelniające, którymi go podpisano, zostaną unieważnione, usunięte lub dezaktywowane albo gdy polityka bucketu odrzuci żądanie. Jeśli musisz anulować pojedyncze linki, postaw przed bucketem aplikację, która wydaje krótkotrwałe URL dopiero po własnych kontrolach.
Czy presigned URL w S3 są bezpieczne?
Podpisu nie da się podrobić ani zmienić, a bucket pozostaje prywatny. AWS opisuje jednak presigned URL jako tokeny na okaziciela (bearer tokens): każdy, kto zdobędzie link, może go używać dowolnie wiele razy aż do wygaśnięcia. Ustawiaj krótki czas ważności, używaj HTTPS i nie publikuj linków tam, gdzie można je skopiować.
Czy presigned URL działają z Cloudflare R2, Backblaze B2 lub Wasabi?
Dostawcy, którzy implementują S3 API z Signature Version 4, zazwyczaj je obsługują, a ty używasz tych samych wywołań SDK z endpointem dostawcy. Cloudflare R2 na przykład obsługuje podpisane GET, HEAD, PUT i DELETE z ważnością od 1 sekundy do 7 dni, ale nie obsługuje przesyłania formularzem POST ani własnych domen.
Dlaczego mój presigned URL zwraca SignatureDoesNotMatch?
AWS wymienia trzy częste przyczyny: niezsynchronizowany zegar komputera, proxy zmieniające nagłówki lub query string oraz żądanie, którego metoda, nagłówki lub parametry różnią się od podpisanych. Przy przesyłaniu zwykle winny jest inny Content-Type niż ten użyty przy generowaniu URL.
Źródła
Ceny i limity pochodzą z poniższych oficjalnych stron. Dostawcy je zmieniają, więc sprawdź je, zanim się zdecydujesz.
- Amazon S3: pobieranie i przesyłanie obiektów za pomocą presigned URL
- AWS IAM: metody uwierzytelniania (parametry zapytania SigV4)
- Amazon S3 API: uwierzytelnianie żądań parametrami zapytania (SigV4)
- Dokumentacja AWS CLI: s3 presign
- Dokumentacja Boto3: generate_presigned_url
- AWS SDK for JavaScript v3: @aws-sdk/s3-request-presigner
- Amazon S3: limity przesyłania multipart
- AWS SDKs and Tools: ochrona integralności danych w Amazon S3
- Cloudflare R2: presigned URL
- Backblaze: czy API zgodne z S3 w B2 obsługuje presigned URL?