Une URL présignée S3 est un lien temporaire vers un objet d’un bucket privé. Elle porte une signature dans sa chaîne de requête : quiconque la détient peut télécharger (ou envoyer) cet objet sans identifiants AWS jusqu’à l’expiration du lien, et le bucket ne devient jamais public. Vous la créez avec vos propres clés, dans votre code ou depuis l’AWS CLI, et elle peut durer de 1 seconde à 7 jours. Ce guide explique son fonctionnement, le sens de chaque paramètre X-Amz-*, sa durée de vie possible, le code pour les téléchargements et les envois et les erreurs que vous risquez le plus de rencontrer. Les limites proviennent de la documentation d’AWS et de Cloudflare en octobre 2026.
Les URL présignées S3 en un coup d’œil
| Question | Réponse |
|---|---|
| Que permet-elle ? | Une opération (GET, PUT, HEAD, DELETE…) sur un objet |
| Expiration maximale | 7 jours (604 800 secondes) avec SigV4 et des clés d’utilisateur IAM |
| Expiration avec des clés temporaires | Prend fin avec la session du rôle ou les identifiants STS, selon ce qui arrive en premier |
| Plage dans la console S3 | De 1 minute à 12 heures |
| Expiration par défaut | 3 600 s dans l’AWS CLI et boto3, 900 s dans le presigner JavaScript v3 |
| Peut-elle servir plusieurs fois ? | Oui, autant que l’on veut jusqu’à son expiration |
| Peut-on révoquer une URL ? | Non, seulement en révoquant les identifiants signataires ou en la refusant dans une politique |
| La créer appelle-t-il S3 ? | Non, la signature est calculée localement |
Comment fonctionne une URL présignée
- Votre code (ou la CLI) prend le bucket, la clé de l’objet, la méthode HTTP et une durée d’expiration.
- Le SDK signe ces valeurs avec votre clé d’accès secrète grâce à AWS Signature Version 4. Rien n’est envoyé à S3 à ce stade.
- Vous donnez l’URL obtenue à quelqu’un : un navigateur, un collègue, une application mobile.
- Quand elle est utilisée, S3 recalcule la signature avec la clé secrète associée à la clé d’accès présente dans l’URL. Si elle correspond, que l’URL n’a pas expiré et que le signataire a le droit d’effectuer cette opération, la requête aboutit.
L’URL porte les permissions de celui qui l’a signée. AWS est explicite là-dessus : si l’utilisateur ou le rôle IAM qui a généré l’URL ne peut pas lire l’objet, l’URL renvoie 403, aussi valide que soit la signature.
Ce que signifient les paramètres X-Amz
Une URL présignée SigV4 ressemble à ceci (retours à la ligne ajoutés) :
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
| Paramètre | Signification |
|---|---|
X-Amz-Algorithm | L’algorithme de signature : AWS4-HMAC-SHA256 pour SigV4 |
X-Amz-Credential | ID de clé d’accès plus la portée : <key-id>/<YYYYMMDD>/<region>/s3/aws4_request |
X-Amz-Date | Le moment de la signature, en UTC, yyyyMMddTHHmmssZ |
X-Amz-Expires | Le nombre de secondes de validité après X-Amz-Date, de 1 à 604800 |
X-Amz-SignedHeaders | Les en-têtes couverts par la signature, toujours au moins host |
X-Amz-Signature | La signature, 64 caractères hexadécimaux |
X-Amz-Security-Token | Présent uniquement si l’URL a été signée avec des identifiants temporaires (STS, rôles, EC2, ECS, SSO) |
Modifiez une partie signée (la clé, la méthode, l’expiration ou un en-tête signé) et la signature ne correspond plus. Notez aussi ce que l’URL révèle en clair : le nom du bucket, la clé complète de l’objet et votre ID de clé d’accès. La clé secrète ne figure jamais dans l’URL.
Combien de temps dure une URL présignée
La limite dépend des identifiants qui la signent :
| Signée avec | Validité maximale |
|---|---|
| Clés d’accès d’utilisateur IAM (SigV4) | 7 jours, car la clé de signature SigV4 elle-même est valide 7 jours |
| Session de rôle IAM (STS AssumeRole) | Jusqu’à la fin de la session, 1 heure par défaut |
| Profil d’instance EC2 | Jusqu’à la rotation des identifiants du rôle, environ 6 heures au maximum |
| Rôle de tâche ECS | Jusqu’à la rotation des identifiants de la tâche, généralement toutes les 1 à 6 heures |
| Identifiants révoqués ou supprimés | S’arrête immédiatement, quoi que dise X-Amz-Expires |
| Console S3 | Vous choisissez entre 1 minute et 12 heures |
C’est l’explication de la surprise la plus fréquente : vous demandez 7 jours depuis une fonction Lambda ou une instance EC2 et le lien meurt au bout d’une heure ou de quelques heures. L’URL ne vit pas plus longtemps que les identifiants qui la soutiennent. Si vous avez besoin de liens longs, signez avec un utilisateur IAM dédié qui n’a accès en lecture qu’à ce bucket.
S3 vérifie l’expiration au début d’une requête. Un gros téléchargement qui commence une seconde avant l’échéance se termine normalement, mais une nouvelle tentative après l’échéance échoue avec AccessDenied et le message « Request has expired ». Demander plus de 604 800 secondes est refusé d’emblée.
Comment générer une URL présignée
AWS CLI
aws s3 presign s3://my-bucket/reports/q3.pdf --expires-in 3600
La commande signe une URL de téléchargement (GET). La valeur par défaut est de 3 600 secondes et le maximum de 604 800. Les versions récentes de la CLI utilisent toujours SigV4 : assurez-vous donc qu’une région est configurée. Pour les URL d’envoi, il vous faut 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,
)
Définissez explicitement signature_version='s3v4'. La référence de boto3 avertit que, pour des raisons de rétrocompatibilité, il peut sinon signer avec l’ancienne Signature Version 2 dans les régions qui l’acceptent encore, ce qui supprime silencieusement certains paramètres et en-têtes comme Range. ResponseContentDisposition est facultatif : il fait enregistrer le fichier par le navigateur sous un nom propre au lieu de l’ouvrir directement.
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 vous omettez expiresIn, le presigner JavaScript utilise 900 secondes (15 minutes).
URL d’envoi présignées (PUT)
Le même principe fonctionne dans l’autre sens. Vous signez un PutObject pour une clé et quiconque possède l’URL peut écrire cet objet. Le fichier va directement de son appareil au bucket et votre serveur ne manipule jamais les octets. Si un objet avec cette clé existe déjà, il est remplacé.
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,
})
Ajouter content-type à signableHeaders lie l’URL à ce type : un envoi avec un autre Content-Type est refusé. Dans boto3, l’équivalent est generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}), et le client doit envoyer le même en-tête.
Trois points à vérifier avant d’envoyer depuis un navigateur :
- CORS. Le bucket a besoin d’une règle CORS qui autorise votre origine et la méthode
PUT. Pour les envois multipart, elle doit aussi exposer l’en-têteETag, car le navigateur doit le lire pour chaque partie. - Clé fixe. Générez une clé unique sur le serveur (par exemple avec un préfixe UUID) pour qu’un visiteur ne puisse pas écraser le fichier de quelqu’un d’autre.
- Sommes de contrôle. Les SDK AWS actuels ajoutent par défaut une somme de contrôle CRC aux envois. Si les envois présignés échouent chez un fournisseur compatible S3 avec des erreurs de signature ou de somme de contrôle, définissez
requestChecksumCalculation: 'WHEN_REQUIRED'sur le client.
Le POST présigné est un mécanisme distinct pour les formulaires HTML simples, avec une politique qui peut limiter la taille et le type de contenu. Tous les fournisseurs compatibles S3 ne le prennent pas en charge (R2 non), le PUT présigné est donc le choix le plus portable.
Gros fichiers : envois multipart présignés
Un PUT unique vers S3 est plafonné à 5 Go, et AWS conseille le multipart à partir d’environ 100 Mo. Le multipart découpe le fichier en 10 000 parties au maximum, chacune de 5 Mio à 5 Gio sauf la dernière, envoyées indépendamment, en parallèle et avec des nouvelles tentatives par partie. La présignature se fait par partie : le serveur démarre l’envoi, signe une URL UploadPart par partie, le navigateur envoie chaque morceau et renvoie son ETag, puis le serveur termine l’envoi.
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 }] },
})
)
Ajoutez une règle de cycle de vie qui annule les envois multipart inachevés au bout de quelques jours, sinon les parties abandonnées continuent d’occuper du stockage facturable.
Les URL présignées sur R2, B2, Spaces et Wasabi
Les fournisseurs qui implémentent l’API S3 avec SigV4 acceptent le même code : vous ne changez que l’endpoint, la région et les clés. Les détails diffèrent, vérifiez donc la documentation du fournisseur :
- Cloudflare R2 prend en charge
GET,HEAD,PUTetDELETEprésignés, avec une expiration de 1 seconde à 7 jours. Il ne prend pas en charge lePOSTprésigné, et les URL présignées ne fonctionnent que sur le domaine de l’API S3 (<ACCOUNT_ID>.r2.cloudflarestorage.com), pas sur les domaines personnalisés. Utilisezregion: 'auto'. - Backblaze B2 indique que son API compatible S3 prend en charge les URL présignées générées avec l’AWS CLI ou les SDK.
- DigitalOcean Spaces et Wasabi fonctionnent avec les mêmes appels de SDK pointés vers leurs endpoints.
Pour les prix de chaque fournisseur, voir le comparatif de S3, R2, Spaces, Wasabi et B2.
Les erreurs courantes des URL présignées et leurs solutions
| Erreur | Cause habituelle | Solution |
|---|---|---|
SignatureDoesNotMatch | Méthode, en-têtes ou requête différents de ce qui a été signé ; horloge désynchronisée | Envoyez le même Content-Type et les mêmes en-têtes ; synchronisez l’horloge avec NTP |
AccessDenied « Request has expired » | L’URL a dépassé X-Amz-Date + X-Amz-Expires | Générez une nouvelle URL ; réduisez le délai entre signature et utilisation |
| L’URL expire plus tôt que demandé | Signée avec des identifiants temporaires (rôle, EC2, ECS, Lambda) | Signez avec des identifiants qui vivent aussi longtemps que le lien doit durer |
ExpiredToken | La session STS derrière X-Amz-Security-Token est terminée | Rafraîchissez les identifiants avant de signer |
403 AccessDenied sur une URL récente | Le signataire n’a pas s3:GetObject ou s3:PutObject, ou une politique le refuse | Accordez la permission au principal signataire ; vérifiez les politiques de bucket |
AuthorizationQueryParametersError | Expiration supérieure à 604 800 secondes ou portée d’identifiants mal formée | Gardez X-Amz-Expires à 7 jours ou moins ; signez dans la région du bucket |
| Navigateur bloqué par CORS | Aucune règle CORS pour votre origine et votre méthode sur le bucket | Ajoutez une règle CORS avec votre origine, GET/PUT et ETag exposé |
AccessDenied avec HeadersNotSigned | Range a été signé mais If-Range envoyé sans signature | Ajoutez If-Range aux en-têtes signés |
Les proxys d’entreprise qui réécrivent les en-têtes ou les chaînes de requête cassent aussi les signatures. Si une URL fonctionne depuis un réseau et pas depuis un autre, testez sans le proxy.
Bonnes pratiques de sécurité
AWS qualifie les URL présignées de jetons au porteur et recommande de les protéger. En pratique :
- Gardez-les courtes. Quelques minutes pour les envois et les fichiers sensibles, quelques heures pour les téléchargements ordinaires. Générez-en une nouvelle quand quelqu’un a de nouveau besoin du fichier.
- Signez avec une identité restreinte. Un utilisateur ou un rôle IAM dédié qui ne peut que lire (ou que écrire) le préfixe concerné limite les dégâts si un lien fuite.
- Ajoutez des garde-fous dans la politique du bucket. La condition
s3:signatureAgerejette les signatures plus anciennes qu’un âge donné, etaws:SourceIprestreint les réseaux qui peuvent les utiliser. - Ne les journalisez pas. Des URL présignées complètes dans les journaux d’application, les outils d’analyse ou l’historique d’une messagerie sont des identifiants valides jusqu’à leur expiration.
- Prévoyez ce qui leur manque. Il n’y a ni mot de passe, ni compteur de téléchargements, ni révocation par lien : placez une application devant quand vous en avez besoin.
Pour le partage en détail, avec des options qui ajoutent ces contrôles, voir comment partager des fichiers S3 sans rendre le bucket public.
Comment Nubbo utilise les URL présignées
Nubbo repose sur ce principe. Quand vous envoyez un fichier dans l’explorateur, le serveur signe un PUT (ou, à partir de 100 Mo, un envoi multipart avec trois parties en parallèle) et votre navigateur envoie les octets directement à votre bucket. Les téléchargements de fichiers individuels utilisent des URL GET présignées valides une heure. Vos clés d’accès sont stockées chiffrées en AES-256-GCM et ne servent que sur le serveur, pour signer.
Les liens de partage ajoutent ce qui manque aux URL présignées brutes : un mot de passe, une date d’expiration et une limite de téléchargements facultatifs que Nubbo vérifie avant de délivrer une URL de courte durée, et vous pouvez désactiver un lien à tout moment. Les téléchargements ZIP de plusieurs fichiers sont assemblés dans votre navigateur, qui télécharge chaque fichier directement depuis votre bucket : Nubbo ne voit jamais le contenu. Une seule exception, en toute transparence : les miniatures sont écrites dans votre propre bucket sous .nubbo-thumbs/. Cela fonctionne de la même façon sur Amazon S3, Cloudflare R2, DigitalOcean Spaces, Wasabi, Backblaze B2 et les endpoints publics compatibles S3.
Questions fréquentes
Qu'est-ce qu'une URL présignée dans S3 ?
C'est un lien HTTPS normal vers un objet d'un bucket privé, avec une signature ajoutée à la chaîne de requête. Quiconque possède le lien peut effectuer l'unique opération pour laquelle il a été signé, généralement un téléchargement (GET) ou un envoi (PUT), jusqu'à son expiration. Le bucket reste privé et la personne qui utilise le lien n'a besoin d'aucun identifiant AWS.
Quelle est la durée d'expiration maximale d'une URL présignée S3 ?
Sept jours (604 800 secondes) lorsque vous signez avec Signature Version 4 et des clés d'utilisateur IAM à long terme. Une URL signée avec des identifiants temporaires, comme un rôle assumé ou un profil d'instance EC2, cesse de fonctionner quand ces identifiants expirent, même si vous avez demandé plus longtemps. Dans la console S3, vous pouvez choisir entre 1 minute et 12 heures.
Que signifient X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders et X-Amz-Signature ?
Ce sont les paramètres d'authentification SigV4. Algorithm est la méthode de signature (AWS4-HMAC-SHA256), Credential est votre ID de clé d'accès avec la portée date, région et service, Date est le moment de la signature, Expires le nombre de secondes de validité, SignedHeaders la liste des en-têtes couverts par la signature et Signature la signature elle-même. X-Amz-Security-Token n'apparaît que si l'URL a été signée avec des identifiants temporaires.
Puis-je révoquer une URL présignée S3 avant son expiration ?
Pas une par une. Une URL présignée ne cesse de fonctionner plus tôt que si les identifiants qui l'ont signée sont révoqués, supprimés ou désactivés, ou si une politique de bucket refuse la requête. Si vous devez annuler des liens individuellement, placez devant une application qui ne délivre des URL de courte durée qu'après ses propres vérifications.
Les URL présignées S3 sont-elles sûres ?
La signature ne peut être ni falsifiée ni modifiée, et le bucket reste privé. Mais AWS décrit les URL présignées comme des jetons au porteur : quiconque obtient le lien peut l'utiliser, autant de fois qu'il le veut, jusqu'à son expiration. Gardez des durées courtes, utilisez HTTPS et évitez de publier les liens là où ils peuvent être copiés.
Les URL présignées fonctionnent-elles avec Cloudflare R2, Backblaze B2 ou Wasabi ?
Les fournisseurs qui implémentent l'API S3 avec Signature Version 4 les prennent généralement en charge, et vous utilisez les mêmes appels de SDK avec l'endpoint du fournisseur. Cloudflare R2, par exemple, prend en charge GET, HEAD, PUT et DELETE présignés avec une expiration de 1 seconde à 7 jours, mais pas les envois par formulaire POST ni les domaines personnalisés.
Pourquoi mon URL présignée renvoie-t-elle SignatureDoesNotMatch ?
AWS cite trois causes courantes : une horloge d'ordinateur désynchronisée, un proxy qui modifie les en-têtes ou la chaîne de requête, et une requête dont la méthode, les en-têtes ou les paramètres diffèrent de ceux qui ont été signés. Pour les envois, le coupable habituel est un Content-Type différent de celui utilisé pour générer l'URL.
Sources
Les prix et les limites proviennent des pages officielles ci-dessous. Les fournisseurs les modifient, alors vérifiez avant de vous engager.
- Amazon S3 : télécharger et envoyer des objets avec des URL présignées
- AWS IAM : méthodes d'authentification (paramètres de requête SigV4)
- API Amazon S3 : authentifier des requêtes avec des paramètres de requête (SigV4)
- Référence de l'AWS CLI : s3 presign
- Référence de Boto3 : generate_presigned_url
- AWS SDK for JavaScript v3 : @aws-sdk/s3-request-presigner
- Amazon S3 : limites de l'envoi multipart
- AWS SDKs and Tools : protections de l'intégrité des données pour Amazon S3
- Cloudflare R2 : URL présignées
- Backblaze : l'API compatible S3 de B2 prend-elle en charge les URL présignées ?