Articolo

URL prefirmati di S3: cosa sono, come funzionano, limiti di scadenza ed esempi di codice

Cos'è un URL prefirmato di S3, cosa indica ogni parametro X-Amz, il limite di 7 giorni, codice per caricare e scaricare (CLI, boto3, JS) ed errori comuni.

Un URL prefirmato di S3 è un link temporaneo a un oggetto di un bucket privato. Porta una firma nella query string, quindi chi lo possiede può scaricare (o caricare) quell’oggetto senza credenziali AWS finché il link non scade, e il bucket non diventa mai pubblico. Lo crei con le tue chiavi, nel tuo codice o dalla AWS CLI, e può durare da 1 secondo fino a 7 giorni. Questa guida spiega come funziona, cosa significa ogni parametro X-Amz-*, quanto può durare, il codice per download e caricamenti e gli errori che è più probabile incontrare. I limiti sono presi dalla documentazione di AWS e Cloudflare, aggiornata a ottobre 2026.

Gli URL prefirmati di S3 in sintesi

DomandaRisposta
Cosa concede?Un’operazione (GET, PUT, HEAD, DELETE…) su un oggetto
Scadenza massima7 giorni (604.800 secondi) con SigV4 e chiavi di un utente IAM
Scadenza con chiavi temporaneeFinisce quando finisce la sessione del ruolo o le credenziali STS, la prima delle due
Intervallo nella console di S3Da 1 minuto a 12 ore
Scadenza predefinita3.600 s nella AWS CLI e in boto3, 900 s nel presigner JavaScript v3
Si può usare più volte?Sì, quante volte si vuole finché non scade
Si può revocare un singolo URL?No, solo revocando le credenziali che firmano o negandolo in una policy
Crearne uno chiama S3?No, la firma viene calcolata in locale

Come funziona un URL prefirmato

  1. Il tuo codice (o la CLI) prende il bucket, la chiave dell’oggetto, il metodo HTTP e un tempo di scadenza.
  2. L’SDK firma questi valori con la tua secret access key usando AWS Signature Version 4. In questo momento non viene inviato nulla a S3.
  3. Dai l’URL risultante a qualcuno: un browser, un collega, un’app mobile.
  4. Quando lo usa, S3 ricalcola la firma con il secret che appartiene alla access key presente nell’URL. Se coincide, l’URL non è scaduto e chi ha firmato può eseguire quell’operazione, la richiesta va a buon fine.

L’URL porta con sé i permessi di chi lo ha firmato. AWS lo dice in modo esplicito: se l’utente o il ruolo IAM che ha generato l’URL non può leggere l’oggetto, l’URL restituisce 403 per quanto valida sia la firma.

Cosa significano i parametri X-Amz

Un URL prefirmato SigV4 ha questo aspetto (a capo aggiunti):

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
ParametroSignificato
X-Amz-AlgorithmL’algoritmo di firma: AWS4-HMAC-SHA256 per SigV4
X-Amz-CredentialID della access key più l’ambito: <key-id>/<YYYYMMDD>/<region>/s3/aws4_request
X-Amz-DateQuando l’URL è stato firmato, in UTC, yyyyMMddTHHmmssZ
X-Amz-ExpiresPer quanti secondi l’URL è valido dopo X-Amz-Date, da 1 a 604800
X-Amz-SignedHeadersIntestazioni coperte dalla firma, sempre almeno host
X-Amz-SignatureLa firma, 64 caratteri esadecimali
X-Amz-Security-TokenPresente solo quando l’URL è stato firmato con credenziali temporanee (STS, ruoli, EC2, ECS, SSO)

Se modifichi qualsiasi parte firmata (la chiave, il metodo, la scadenza o un’intestazione firmata) la firma non coincide più. Nota anche cosa rivela l’URL in chiaro: il nome del bucket, la chiave completa dell’oggetto e l’ID della tua access key. La secret key non è mai nell’URL.

Quanto dura un URL prefirmato

Il limite dipende dalle credenziali che lo firmano:

Firmato conValidità massima
Access key di un utente IAM (SigV4)7 giorni, perché la stessa chiave di firma SigV4 è valida per 7 giorni
Sessione di un ruolo IAM (STS AssumeRole)Fino alla fine della sessione, 1 ora per impostazione predefinita
Instance profile di EC2Fino alla rotazione delle credenziali del ruolo, al massimo circa 6 ore
Task role di ECSFino alla rotazione delle credenziali del task, di solito ogni 1-6 ore
Credenziali revocate o eliminateSmette subito, qualunque cosa dica X-Amz-Expires
Console di S3Scegli tra 1 minuto e 12 ore

Questo spiega la sorpresa più comune: chiedi 7 giorni da una funzione Lambda o da un’istanza EC2 e il link muore dopo un’ora o qualche ora. L’URL dura solo quanto le credenziali che ci stanno dietro. Se ti servono link lunghi, firma con un utente IAM dedicato che ha accesso in lettura solo a quel bucket.

S3 controlla la scadenza quando una richiesta inizia. Un download grande che parte un secondo prima del limite finisce normalmente, ma un nuovo tentativo dopo il limite fallisce con AccessDenied e il messaggio “Request has expired”. Chiedere più di 604.800 secondi viene rifiutato subito.

Come generare un URL prefirmato

AWS CLI

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

Il comando firma un URL di download (GET). Il valore predefinito è 3.600 secondi e il massimo 604.800. Le versioni recenti della CLI usano sempre SigV4, quindi assicurati di avere una regione configurata. Per gli URL di caricamento ti serve 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,
)

Imposta signature_version='s3v4' in modo esplicito. Il riferimento di boto3 avverte che, per compatibilità con il passato, altrimenti può firmare con la vecchia Signature Version 2 nelle regioni che la accettano ancora, il che scarta in silenzio alcuni parametri e intestazioni come Range. ResponseContentDisposition è facoltativo: fa sì che il browser salvi il file con un nome pulito invece di aprirlo nella pagina.

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

Se ometti expiresIn, il presigner JavaScript usa 900 secondi (15 minuti).

URL prefirmati per il caricamento (PUT)

La stessa idea funziona nella direzione opposta. Firmi un PutObject per una chiave e chi ha l’URL può scrivere quell’oggetto. Il file va direttamente dal suo dispositivo al bucket e il tuo server non tocca mai i byte. Se esiste già un oggetto con quella chiave, viene sostituito.

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

Aggiungere content-type a signableHeaders lega l’URL a quel tipo, quindi un caricamento con un Content-Type diverso viene rifiutato. In boto3 l’equivalente è generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}), e il client deve inviare la stessa intestazione.

Tre cose da controllare prima di caricare da un browser:

  • CORS. Il bucket ha bisogno di una regola CORS che consenta la tua origine e il metodo PUT. Per i caricamenti multipart deve anche esporre l’intestazione ETag, perché il browser deve leggerla da ogni parte.
  • Chiave fissa. Genera una chiave univoca sul server (per esempio con un prefisso UUID) così un visitatore non può sovrascrivere il file di un altro.
  • Checksum. Gli SDK AWS attuali aggiungono di default un checksum CRC ai caricamenti. Se i caricamenti prefirmati verso un provider compatibile con S3 falliscono con errori di firma o di checksum, imposta requestChecksumCalculation: 'WHEN_REQUIRED' sul client.

Il POST prefirmato è un meccanismo separato per i normali form HTML, con una policy che può limitare dimensione e tipo di contenuto. Non tutti i provider compatibili con S3 lo supportano (R2 no), quindi il PUT prefirmato è la scelta più portabile.

File grandi: caricamenti multipart prefirmati

Un singolo PUT su S3 ha un limite di 5 GB, e AWS suggerisce il multipart a partire da circa 100 MB. Il multipart divide il file in un massimo di 10.000 parti, ognuna da 5 MiB a 5 GiB tranne l’ultima, che si caricano in modo indipendente, in parallelo e con nuovi tentativi per parte. La prefirma funziona per parte: il server avvia il caricamento, firma un URL UploadPart per ogni parte, il browser invia ogni blocco e restituisce il suo ETag, e il server completa il caricamento.

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

Aggiungi una regola del ciclo di vita che annulli i caricamenti multipart incompleti dopo qualche giorno, altrimenti le parti abbandonate continuano a occupare storage fatturabile.

URL prefirmati su R2, B2, Spaces e Wasabi

I provider che implementano la S3 API con SigV4 accettano lo stesso codice: cambi solo l’endpoint, la regione e le chiavi. I dettagli variano, quindi controlla la documentazione del provider:

  • Cloudflare R2 supporta GET, HEAD, PUT e DELETE prefirmati, con una scadenza da 1 secondo a 7 giorni. Non supporta il POST prefirmato, e gli URL prefirmati funzionano solo sul dominio della S3 API (<ACCOUNT_ID>.r2.cloudflarestorage.com), non sui domini personalizzati. Usa region: 'auto'.
  • Backblaze B2 dichiara che la sua API compatibile con S3 supporta gli URL prefirmati generati con la AWS CLI o con gli SDK.
  • DigitalOcean Spaces e Wasabi funzionano con le stesse chiamate SDK puntate ai loro endpoint.

Per i prezzi di ogni provider, vedi il confronto tra S3, R2, Spaces, Wasabi e B2.

Errori comuni degli URL prefirmati e come risolverli

ErroreCausa abitualeSoluzione
SignatureDoesNotMatchMetodo, intestazioni o query diversi da quelli firmati; orologio non sincronizzatoInvia lo stesso Content-Type e le stesse intestazioni; sincronizza l’orologio con NTP
AccessDenied “Request has expired”L’URL ha superato X-Amz-Date + X-Amz-ExpiresGenera un nuovo URL; riduci il tempo tra firma e uso
L’URL scade prima del previstoFirmato con credenziali temporanee (ruolo, EC2, ECS, Lambda)Firma con credenziali che durano quanto deve durare il link
ExpiredTokenLa sessione STS dietro X-Amz-Security-Token è terminataAggiorna le credenziali prima di firmare
403 AccessDenied su un URL appena creatoChi firma non ha s3:GetObject o s3:PutObject, o una policy lo negaConcedi il permesso a chi firma; controlla le bucket policy
AuthorizationQueryParametersErrorScadenza oltre 604.800 secondi o ambito della credenziale malformatoMantieni X-Amz-Expires a 7 giorni o meno; firma nella regione del bucket
Browser bloccato da CORSNessuna regola CORS sul bucket per la tua origine e il tuo metodoAggiungi una regola CORS con la tua origine, GET/PUT ed ETag esposto
AccessDenied con HeadersNotSignedRange era firmato ma If-Range è stato inviato senza firmaAggiungi If-Range alle intestazioni firmate

Anche i proxy aziendali che riscrivono intestazioni o query string rompono le firme. Se un URL funziona da una rete e non da un’altra, prova senza il proxy.

Buone pratiche di sicurezza

AWS chiama gli URL prefirmati bearer token e consiglia di proteggerli. In pratica:

  • Tienili brevi. Minuti per caricamenti e file sensibili, ore per i download normali. Generane uno nuovo quando qualcuno ha di nuovo bisogno del file.
  • Firma con un’identità ristretta. Un utente o un ruolo IAM dedicato che può solo leggere (o solo scrivere) il prefisso in questione limita i danni se un link trapela.
  • Aggiungi protezioni nella bucket policy. La condizione s3:signatureAge rifiuta le firme più vecchie di un’età stabilita, e aws:SourceIp limita le reti che possono usarle.
  • Non registrarli nei log. Gli URL prefirmati completi nei log delle applicazioni, nelle analisi o nella cronologia delle chat sono credenziali funzionanti finché non scadono.
  • Tieni conto di ciò che manca loro. Non c’è password, né conteggio dei download, né revoca per singolo link, quindi metti davanti un’applicazione quando ti servono.

Per approfondire la condivisione, con opzioni che aggiungono questi controlli, vedi come condividere file S3 senza rendere pubblico il bucket.

Come Nubbo usa gli URL prefirmati

Nubbo è costruito su questo schema. Quando carichi nell’esploratore, il server firma un PUT (o, da 100 MB, un caricamento multipart con tre parti in parallelo) e il tuo browser invia i byte direttamente al tuo bucket. I download di singoli file usano URL GET prefirmati validi per un’ora. Le tue access key sono salvate cifrate con AES-256-GCM e vengono usate solo sul server per firmare.

I link di condivisione aggiungono ciò che manca agli URL prefirmati puri: password, data di scadenza e limite di download facoltativi che Nubbo controlla prima di rilasciare un URL di breve durata, e puoi disattivare un link in qualsiasi momento. I download ZIP di più file vengono creati nel tuo browser, che scarica ogni file direttamente dal tuo bucket: Nubbo non vede mai il contenuto. Un’eccezione, per onestà: le miniature vengono scritte nel tuo stesso bucket sotto .nubbo-thumbs/. Funziona allo stesso modo su Amazon S3, Cloudflare R2, DigitalOcean Spaces, Wasabi, Backblaze B2 ed endpoint pubblici compatibili con S3.

Domande frequenti

Cos'è un URL prefirmato in S3?

È un normale link HTTPS a un oggetto di un bucket privato con una firma aggiunta alla query string. Chi ha il link può eseguire la singola operazione per cui è stato firmato, di solito un download (GET) o un caricamento (PUT), finché non scade. Il bucket resta privato e chi usa il link non ha bisogno di credenziali AWS.

Qual è la scadenza massima di un URL prefirmato di S3?

Sette giorni (604.800 secondi) quando firmi con Signature Version 4 e chiavi di un utente IAM a lungo termine. Un URL firmato con credenziali temporanee, come un ruolo assunto o un instance profile di EC2, smette di funzionare quando quelle credenziali scadono, anche se hai chiesto di più. Nella console di S3 puoi scegliere tra 1 minuto e 12 ore.

Cosa significano X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders e X-Amz-Signature?

Sono i parametri di autenticazione SigV4. Algorithm è il metodo di firma (AWS4-HMAC-SHA256), Credential è l'ID della tua access key più l'ambito di data, regione e servizio, Date è quando l'URL è stato firmato, Expires è per quanti secondi resta valido, SignedHeaders elenca le intestazioni coperte dalla firma e Signature è la firma stessa. X-Amz-Security-Token compare solo quando l'URL è stato firmato con credenziali temporanee.

Posso revocare un URL prefirmato di S3 prima che scada?

Non uno per uno. Un URL prefirmato smette di funzionare prima solo quando le credenziali che lo hanno firmato vengono revocate, eliminate o disattivate, o quando una bucket policy nega la richiesta. Se devi annullare singoli link, metti davanti un'applicazione che rilascia URL di breve durata solo dopo i propri controlli.

Gli URL prefirmati di S3 sono sicuri?

La firma non può essere falsificata né alterata, e il bucket resta privato. Però AWS descrive gli URL prefirmati come bearer token: chiunque ottenga il link può usarlo, tutte le volte che vuole, finché non scade. Mantieni scadenze brevi, usa HTTPS ed evita di pubblicare i link dove possono essere copiati.

Gli URL prefirmati funzionano con Cloudflare R2, Backblaze B2 o Wasabi?

I provider che implementano la S3 API con Signature Version 4 in genere li supportano, e usi le stesse chiamate SDK con l'endpoint del provider. Cloudflare R2, per esempio, supporta GET, HEAD, PUT e DELETE prefirmati con una scadenza da 1 secondo a 7 giorni, ma non i caricamenti con form POST né i domini personalizzati.

Perché il mio URL prefirmato restituisce SignatureDoesNotMatch?

AWS elenca tre cause comuni: l'orologio del computer non sincronizzato, un proxy che modifica le intestazioni o la query string e una richiesta il cui metodo, intestazioni o parametri sono diversi da quelli firmati. Nei caricamenti il colpevole tipico è inviare un Content-Type diverso da quello usato quando è stato generato l'URL.

Fonti

Prezzi e limiti provengono dalle pagine ufficiali qui sotto. I provider li cambiano, quindi controllali prima di decidere.