Article

URL presignades d'S3: què són, com funcionen, límits de caducitat i exemples de codi

Què és una URL presignada d'S3, què vol dir cada paràmetre X-Amz, el límit de 7 dies, codi de pujada i baixada (CLI, boto3, JS) i errors habituals.

Una URL presignada d’S3 és un enllaç temporal a un objecte d’un bucket privat. Porta una signatura a la query string, així que qui la tingui pot baixar (o pujar) aquest objecte sense credencials d’AWS fins que l’enllaç caduqui, i el bucket mai es fa públic. La crees amb les teves pròpies claus, al teu codi o des de l’AWS CLI, i pot durar d’1 segon fins a 7 dies. Aquesta guia explica com funciona, què vol dir cada paràmetre X-Amz-*, quant pot durar, codi per a baixades i pujades i els errors que és més probable que et trobis. Els límits surten de la documentació d’AWS i de Cloudflare a l’octubre de 2026.

URL presignades d’S3 d’un cop d’ull

PreguntaResposta
Què permet?Una operació (GET, PUT, HEAD, DELETE…) sobre un objecte
Caducitat màxima7 dies (604.800 segons) amb SigV4 i claus d’usuari IAM
Caducitat amb claus temporalsS’acaba quan s’acaba la sessió del rol o les credencials STS, el que passi primer
Interval a la consola d’S3D’1 minut a 12 hores
Caducitat per defecte3.600 s a l’AWS CLI i boto3, 900 s al presigner de JavaScript v3
Es pot fer servir diverses vegades?Sí, tantes vegades com es vulgui fins que caduqui
Es pot revocar una URL?No, només revocant les credencials que la signen o denegant-la en una política
Crear-ne una fa una crida a S3?No, la signatura es calcula localment

Com funciona una URL presignada

  1. El teu codi (o la CLI) agafa el bucket, la clau de l’objecte, el mètode HTTP i un temps de caducitat.
  2. L’SDK signa aquests valors amb la teva secret access key fent servir AWS Signature Version 4. En aquest moment no s’envia res a S3.
  3. Dones la URL resultant a algú: un navegador, un company de feina, una app mòbil.
  4. Quan la fa servir, S3 torna a calcular la signatura amb el secret que correspon a l’access key de la URL. Si coincideix, la URL no ha caducat i qui l’ha signat té permís per fer aquesta operació, la petició passa.

La URL porta els permisos de qui la va signar. AWS ho deixa clar: si l’usuari o el rol IAM que va generar la URL no pot llegir l’objecte, la URL retorna 403 per vàlida que sigui la signatura.

Què volen dir els paràmetres X-Amz

Una URL presignada amb SigV4 té aquest aspecte (amb salts de línia afegits):

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
ParàmetreSignificat
X-Amz-AlgorithmL’algorisme de signatura: AWS4-HMAC-SHA256 per a SigV4
X-Amz-CredentialAccess key ID més l’abast: <key-id>/<YYYYMMDD>/<region>/s3/aws4_request
X-Amz-DateQuan es va signar la URL, en UTC, yyyyMMddTHHmmssZ
X-Amz-ExpiresQuants segons és vàlida la URL després de X-Amz-Date, d’1 a 604800
X-Amz-SignedHeadersCapçaleres que cobreix la signatura, sempre com a mínim host
X-Amz-SignatureLa signatura, 64 caràcters hexadecimals
X-Amz-Security-TokenNomés apareix quan la URL s’ha signat amb credencials temporals (STS, rols, EC2, ECS, SSO)

Si canvies qualsevol part signada (la clau, el mètode, la caducitat o una capçalera signada), la signatura deixa de coincidir. Fixa’t també en el que la URL revela en text pla: el nom del bucket, la clau completa de l’objecte i el teu access key ID. La secret key mai és a la URL.

Quant dura una URL presignada

El límit depèn de les credencials que la signen:

Signada ambValidesa màxima
Claus d’accés d’usuari IAM (SigV4)7 dies, perquè la mateixa clau de signatura de SigV4 és vàlida 7 dies
Sessió de rol IAM (STS AssumeRole)Fins que s’acaba la sessió, 1 hora per defecte
Perfil d’instància EC2Fins que roten les credencials del rol, com a màxim unes 6 hores
Rol de tasca ECSFins que roten les credencials de la tasca, normalment cada 1 a 6 hores
Qualsevol credencial revocada o esborradaDeixa de funcionar immediatament, digui el que digui X-Amz-Expires
Consola d’S3Tries entre 1 minut i 12 hores

Això explica la sorpresa més habitual: demanes 7 dies des d’una funció Lambda o una instància EC2 i l’enllaç mor al cap d’una hora o d’unes quantes hores. La URL només dura tant com les credencials que hi ha al darrere. Si necessites enllaços llargs, signa amb un usuari IAM dedicat que només tingui accés de lectura a aquell bucket.

S3 comprova la caducitat quan comença una petició. Una baixada gran que comença un segon abans del termini acaba amb normalitat, però un reintent després del termini falla amb AccessDenied i el missatge «Request has expired». Demanar més de 604.800 segons es rebutja directament.

Com generar una URL presignada

AWS CLI

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

L’ordre signa una URL de baixada (GET). El valor per defecte és de 3.600 segons i el màxim de 604.800. Les versions recents de la CLI sempre fan servir SigV4, així que assegura’t que tens una regió configurada. Per a URL de pujada necessites un 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,
)

Defineix signature_version='s3v4' explícitament. La referència de boto3 avisa que, per compatibilitat amb versions anteriors, si no ho fas pot signar amb l’antiga Signature Version 2 a les regions que encara l’accepten, cosa que descarta sense avisar alguns paràmetres i capçaleres com Range. ResponseContentDisposition és opcional: fa que el navegador desi el fitxer amb un nom net en lloc d’obrir-lo directament.

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 }
)

Si no hi poses expiresIn, el presigner de JavaScript fa servir 900 segons (15 minuts).

URL presignades de pujada (PUT)

La mateixa idea funciona en sentit contrari. Signes un PutObject per a una clau i qui tingui la URL pot escriure aquest objecte. El fitxer va directament del seu dispositiu al bucket i el teu servidor mai toca els bytes. Si ja existeix un objecte amb aquesta clau, se substitueix.

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,
})

Afegir content-type a signableHeaders lliga la URL a aquest tipus, així que una pujada amb un Content-Type diferent es rebutja. A boto3 l’equivalent és generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}), i el client ha d’enviar la mateixa capçalera.

Tres coses que cal comprovar abans de pujar des d’un navegador:

  • CORS. El bucket necessita una regla de CORS que permeti el teu origen i el mètode PUT. Per a les pujades multipart també ha d’exposar la capçalera ETag, perquè el navegador l’ha de llegir de cada part.
  • Clau fixa. Genera una clau única al servidor (per exemple amb un prefix UUID) perquè un visitant no pugui sobreescriure el fitxer d’una altra persona.
  • Sumes de comprovació. Els SDK actuals d’AWS afegeixen per defecte una suma de comprovació CRC a les pujades. Si les pujades presignades fallen contra un proveïdor compatible amb S3 amb errors de signatura o de suma de comprovació, defineix requestChecksumCalculation: 'WHEN_REQUIRED' al client.

El POST presignat és un mecanisme a part per a formularis HTML senzills, amb una política que pot limitar la mida i el tipus de contingut. No tots els proveïdors compatibles amb S3 l’admeten (R2 no), així que el PUT presignat és l’opció més portable.

Fitxers grans: pujades multipart presignades

Un sol PUT a S3 està limitat a 5 GB, i AWS suggereix multipart a partir d’uns 100 MB. Multipart divideix el fitxer en fins a 10.000 parts, cadascuna de 5 MiB a 5 GiB excepte l’última, que es pugen de manera independent, en paral·lel i amb reintents per part. La presignatura funciona per part: el servidor inicia la pujada, signa una URL UploadPart per a cada part, el navegador envia cada tros i en retorna l’ETag, i el servidor completa la pujada.

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 }] },
  })
)

Afegeix una regla de cicle de vida que avorti les pujades multipart incompletes al cap d’uns quants dies, o les parts abandonades continuaran ocupant emmagatzematge facturable.

URL presignades a R2, B2, Spaces i Wasabi

Els proveïdors que implementen l’API S3 amb SigV4 accepten el mateix codi: només canvies l’endpoint, la regió i les claus. Els detalls varien, així que consulta la documentació del proveïdor:

  • Cloudflare R2 admet GET, HEAD, PUT i DELETE presignats, amb una caducitat d’1 segon a 7 dies. No admet el POST presignat, i les URL presignades només funcionen al domini de l’API S3 (<ACCOUNT_ID>.r2.cloudflarestorage.com), no als dominis personalitzats. Fes servir region: 'auto'.
  • Backblaze B2 afirma que la seva API compatible amb S3 admet URL presignades generades amb l’AWS CLI o els SDK.
  • DigitalOcean Spaces i Wasabi funcionen amb les mateixes crides de l’SDK apuntades als seus endpoints.

Per als preus de cada proveïdor, mira la comparativa d’S3, R2, Spaces, Wasabi i B2.

Errors habituals amb URL presignades i com resoldre’ls

ErrorCausa habitualSolució
SignatureDoesNotMatchEl mètode, les capçaleres o la query difereixen del que es va signar; rellotge desincronitzatEnvia el mateix Content-Type i les mateixes capçaleres; sincronitza el rellotge amb NTP
AccessDenied «Request has expired»La URL ha passat de X-Amz-Date + X-Amz-ExpiresGenera una URL nova; escurça el temps entre la signatura i l’ús
La URL caduca abans del que s’ha demanatSignada amb credencials temporals (rol, EC2, ECS, Lambda)Signa amb credencials que durin tant com hagi de durar l’enllaç
ExpiredTokenLa sessió STS que hi ha darrere de X-Amz-Security-Token s’ha acabatRenova les credencials abans de signar
403 AccessDenied en una URL acabada de ferQui signa no té s3:GetObject o s3:PutObject, o una política ho denegaDona el permís a qui signa; revisa les bucket policies
AuthorizationQueryParametersErrorCaducitat de més de 604.800 segons o un abast de credencial mal formatMantén X-Amz-Expires en 7 dies o menys; signa a la regió del bucket
El navegador ho bloqueja per CORSEl bucket no té cap regla de CORS per al teu origen i mètodeAfegeix una regla de CORS amb el teu origen, GET/PUT i l’ETag exposat
AccessDenied amb HeadersNotSignedS’ha signat Range però s’ha enviat If-Range sense signarAfegeix If-Range a les capçaleres signades

Els proxies corporatius que reescriuen capçaleres o query strings també trenquen les signatures. Si una URL funciona des d’una xarxa i no des d’una altra, prova-la sense el proxy.

Bones pràctiques de seguretat

AWS anomena les URL presignades bearer tokens i recomana protegir-les. A la pràctica:

  • Fes-les curtes. Minuts per a pujades i fitxers sensibles, hores per a baixades normals. Genera’n una de nova quan algú torni a necessitar el fitxer.
  • Signa amb una identitat limitada. Un usuari o rol IAM dedicat que només pugui llegir (o només escriure) el prefix en qüestió limita el dany si un enllaç es filtra.
  • Afegeix proteccions a la bucket policy. La condició s3:signatureAge rebutja les signatures més antigues d’una edat determinada, i aws:SourceIp restringeix des de quines xarxes es poden fer servir.
  • No les registris. Les URL presignades completes als registres de l’aplicació, a l’analítica o a l’historial de xat són credencials vàlides fins que caduquen.
  • Tingues en compte el que no tenen. No hi ha contrasenya, ni comptador de baixades ni revocació per enllaç, així que posa-hi davant una aplicació quan necessitis aquestes coses.

Per aprofundir en la part de compartir, amb opcions que afegeixen aquests controls, mira com compartir fitxers d’S3 sense fer públic el bucket.

Com fa servir Nubbo les URL presignades

Nubbo està construït sobre aquest patró. Quan puges fitxers a l’explorador, el servidor signa un PUT (o, a partir de 100 MB, una pujada multipart amb tres parts en paral·lel) i el teu navegador envia els bytes directament al teu bucket. Les baixades d’un sol fitxer fan servir URL GET presignades vàlides durant una hora. Les teves claus d’accés es guarden xifrades amb AES-256-GCM i només es fan servir al servidor per signar.

Els enllaços per compartir afegeixen el que no tenen les URL presignades soles: contrasenya, data de caducitat i límit de baixades opcionals que Nubbo comprova abans d’emetre una URL de curta durada, i pots desactivar un enllaç quan vulguis. Les baixades ZIP de diversos fitxers les munta el teu navegador, que baixa cada fitxer directament del teu bucket: Nubbo no veu el contingut. Una excepció, per ser honestos: les miniatures s’escriuen al teu propi bucket a .nubbo-thumbs/. Funciona igual a Amazon S3, Cloudflare R2, DigitalOcean Spaces, Wasabi, Backblaze B2 i endpoints públics compatibles amb S3.

Preguntes freqüents

Què és una URL presignada a S3?

És un enllaç HTTPS normal a un objecte d'un bucket privat amb una signatura afegida a la query string. Qui tingui l'enllaç pot fer l'única operació per a la qual s'ha signat, normalment una baixada (GET) o una pujada (PUT), fins que caduqui. El bucket continua sent privat i la persona que fa servir l'enllaç no necessita credencials d'AWS.

Quina és la caducitat màxima d'una URL presignada d'S3?

Set dies (604.800 segons) quan signes amb Signature Version 4 i claus d'usuari IAM de llarga durada. Una URL signada amb credencials temporals, com un rol assumit o un perfil d'instància EC2, deixa de funcionar quan caduquen aquestes credencials, encara que n'hagis demanat més. A la consola d'S3 pots triar entre 1 minut i 12 hores.

Què volen dir X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders i X-Amz-Signature?

Són els paràmetres d'autenticació de SigV4. Algorithm és el mètode de signatura (AWS4-HMAC-SHA256), Credential és el teu access key ID més l'abast de data, regió i servei, Date és quan es va signar la URL, Expires és quants segons continua sent vàlida, SignedHeaders llista les capçaleres que cobreix la signatura i Signature és la signatura en si. X-Amz-Security-Token només apareix quan la URL s'ha signat amb credencials temporals.

Puc revocar una URL presignada d'S3 abans que caduqui?

No una per una. Una URL presignada només deixa de funcionar abans d'hora quan les credencials que la van signar es revoquen, s'esborren o es desactiven, o quan una bucket policy denega la petició. Si necessites cancel·lar enllaços concrets, posa-hi davant una aplicació que només lliuri URL de curta durada després de fer les seves pròpies comprovacions.

Les URL presignades d'S3 són segures?

La signatura no es pot falsificar ni alterar, i el bucket continua sent privat. Però AWS descriu les URL presignades com a bearer tokens: qualsevol persona que obtingui l'enllaç el pot fer servir, tantes vegades com vulgui, fins que caduqui. Fes servir caducitats curtes i HTTPS i evita publicar els enllaços on es puguin copiar.

Les URL presignades funcionen amb Cloudflare R2, Backblaze B2 o Wasabi?

Els proveïdors que implementen l'API S3 amb Signature Version 4 normalment les admeten, i fas servir les mateixes crides de l'SDK amb l'endpoint del proveïdor. Cloudflare R2, per exemple, admet GET, HEAD, PUT i DELETE presignats amb una caducitat d'1 segon a 7 dies, però no pujades amb formulari POST ni dominis personalitzats.

Per què la meva URL presignada retorna SignatureDoesNotMatch?

AWS indica tres causes habituals: un rellotge de l'ordinador desincronitzat, un proxy que canvia les capçaleres o la query string, i una petició amb un mètode, capçaleres o paràmetres diferents dels que es van signar. En les pujades, el culpable sol ser enviar un Content-Type diferent del que es va fer servir en generar la URL.

Fonts

Els preus i els límits surten de les pàgines oficials de sota. Els proveïdors els canvien, així que comprova'ls abans de decidir.