Unha URL presinada de S3 é unha ligazón temporal a un obxecto dun bucket privado. Leva unha sinatura na súa query string, así que quen a teña pode descargar (ou subir) ese obxecto sen credenciais de AWS ata que a ligazón caduque, e o bucket nunca se fai público. Créala coas túas propias chaves, no teu código ou desde a AWS CLI, e pode durar desde 1 segundo ata 7 días. Esta guía explica como funciona, que significa cada parámetro X-Amz-*, canto pode durar, código para descargas e subidas e os erros cos que máis probablemente te atoparás. Os límites están tomados da documentación de AWS e de Cloudflare en outubro de 2026.
URL presinadas de S3 dunha ollada
| Pregunta | Resposta |
|---|---|
| Que permite? | Unha operación (GET, PUT, HEAD, DELETE…) sobre un obxecto |
| Caducidade máxima | 7 días (604.800 segundos) con SigV4 e chaves dun usuario de IAM |
| Caducidade con chaves temporais | Remata cando remata a sesión do rol ou as credenciais STS, o que suceda antes |
| Rango na consola de S3 | De 1 minuto a 12 horas |
| Caducidade por defecto | 3.600 s na AWS CLI e boto3, 900 s no presigner de JavaScript v3 |
| Pódese usar varias veces? | Si, tantas veces como se queira ata que caduque |
| Pódese revogar unha URL? | Non, só revogando as credenciais que a asinan ou denegándoa nunha política |
| Crear unha chama a S3? | Non, a sinatura calcúlase localmente |
Como funciona unha URL presinada
- O teu código (ou a CLI) toma o bucket, a chave do obxecto, o método HTTP e un tempo de caducidade.
- O SDK asina eses valores coa túa secret access key usando AWS Signature Version 4. Neste momento non se envía nada a S3.
- Dás a URL resultante a alguén: un navegador, unha compañeira, unha app móbil.
- Cando a usan, S3 volve calcular a sinatura coa secret que corresponde á access key da URL. Se coincide, a URL non caducou e quen a asinou pode facer esa operación, a petición pasa.
A URL leva os permisos de quen a asinou. AWS é explícito niso: se o usuario ou o rol de IAM que xerou a URL non pode ler o obxecto, a URL devolve 403 por moi válida que sexa a sinatura.
Que significan os parámetros X-Amz
Unha URL presinada con SigV4 ten este aspecto (con saltos de liña engadidos):
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ámetro | Significado |
|---|---|
X-Amz-Algorithm | O algoritmo de sinatura: AWS4-HMAC-SHA256 para SigV4 |
X-Amz-Credential | ID da access key máis o ámbito: <key-id>/<YYYYMMDD>/<region>/s3/aws4_request |
X-Amz-Date | Cando se asinou a URL, en UTC, yyyyMMddTHHmmssZ |
X-Amz-Expires | Cantos segundos é válida a URL despois de X-Amz-Date, de 1 a 604800 |
X-Amz-SignedHeaders | Cabeceiras cubertas pola sinatura, sempre polo menos host |
X-Amz-Signature | A sinatura, 64 caracteres hexadecimais |
X-Amz-Security-Token | Só aparece cando a URL se asinou con credenciais temporais (STS, roles, EC2, ECS, SSO) |
Cambia calquera parte asinada (a chave, o método, a caducidade ou unha cabeceira asinada) e a sinatura deixa de coincidir. Fíxate tamén no que a URL revela en texto claro: o nome do bucket, a chave completa do obxecto e o ID da túa access key. A secret key nunca está na URL.
Canto dura unha URL presinada
O límite depende das credenciais que a asinan:
| Asinada con | Validez máxima |
|---|---|
| Access keys dun usuario de IAM (SigV4) | 7 días, porque a propia chave de sinatura de SigV4 vale 7 días |
| Sesión dun rol de IAM (STS AssumeRole) | Ata que remata a sesión, 1 hora por defecto |
| Perfil de instancia EC2 | Ata que rotan as credenciais do rol, como moito unhas 6 horas |
| Rol de tarefa de ECS | Ata que rotan as credenciais da tarefa, normalmente cada 1 a 6 horas |
| Calquera credencial revogada ou borrada | Deixa de funcionar ao momento, diga o que diga X-Amz-Expires |
| Consola de S3 | Escolles entre 1 minuto e 12 horas |
Isto explica a sorpresa máis habitual: pides 7 días desde unha función Lambda ou unha instancia EC2 e a ligazón morre despois dunha hora ou de poucas horas. A URL só dura o que duran as credenciais que ten detrás. Se necesitas ligazóns longas, asina cun usuario de IAM dedicado que só teña acceso de lectura a ese bucket.
S3 comproba a caducidade cando comeza a petición. Unha descarga grande que empeza un segundo antes do prazo remata con normalidade, pero un reintento despois do prazo falla con AccessDenied e a mensaxe “Request has expired”. Pedir máis de 604.800 segundos rexéitase directamente.
Como xerar unha URL presinada
AWS CLI
aws s3 presign s3://my-bucket/reports/q3.pdf --expires-in 3600
O comando asina unha URL de descarga (GET). O valor por defecto son 3.600 segundos e o máximo 604.800. As versións recentes da CLI usan sempre SigV4, así que asegúrate de ter unha rexión configurada. Para URL de subida necesitas 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,
)
Indica signature_version='s3v4' de forma explícita. A referencia de boto3 avisa de que, por compatibilidade, se non pode asinar coa antiga Signature Version 2 nas rexións que aínda a aceptan, o que descarta sen avisar algúns parámetros e cabeceiras como Range. ResponseContentDisposition é opcional: fai que o navegador garde o ficheiro cun nome limpo en vez de abrilo directamente.
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 omites expiresIn, o presigner de JavaScript usa 900 segundos (15 minutos).
URL presinadas de subida (PUT)
A mesma idea funciona na outra dirección. Asinas un PutObject para unha chave e quen teña a URL pode escribir ese obxecto. O ficheiro vai directo desde o seu dispositivo ao bucket e o teu servidor nunca toca os bytes. Se xa existe un obxecto con esa chave, substitúese.
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,
})
Engadir content-type a signableHeaders liga a URL a ese tipo, así que unha subida cun Content-Type distinto rexéitase. En boto3 o equivalente é generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}), e o cliente ten que enviar a mesma cabeceira.
Tres cousas que revisar antes de subir desde un navegador:
- CORS. O bucket necesita unha regra de CORS que permita a túa orixe e o método
PUT. Para subidas multipart tamén ten que expoñer a cabeceiraETag, porque o navegador ten que lela de cada parte. - Chave fixa. Xera no servidor unha chave única (por exemplo cun prefixo UUID) para que un visitante non poida sobrescribir o ficheiro doutra persoa.
- Sumas de comprobación. Os SDK actuais de AWS engaden por defecto unha suma CRC ás subidas. Se as subidas presinadas fallan contra un provedor compatible con S3 con erros de sinatura ou de suma de comprobación, configura
requestChecksumCalculation: 'WHEN_REQUIRED'no cliente.
O POST presinado é un mecanismo distinto para formularios HTML simples, cunha política que pode limitar o tamaño e o tipo de contido. Non todos os provedores compatibles con S3 o admiten (R2 non), así que o PUT presinado é a opción máis portable.
Ficheiros grandes: subidas multipart presinadas
Un único PUT a S3 ten un tope de 5 GB, e AWS suxire multipart a partir duns 100 MB. Multipart divide o ficheiro en ata 10.000 partes, cada unha de 5 MiB a 5 GiB agás a última, que se suben de forma independente, en paralelo e con reintentos por parte. A presinatura funciona por parte: o servidor inicia a subida, asina unha URL UploadPart por cada parte, o navegador envía cada anaco e devolve o seu ETag, e o servidor completa a subida.
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 }] },
})
)
Engade unha regra de ciclo de vida que cancele as subidas multipart incompletas despois duns días, ou as partes abandonadas seguirán ocupando almacenamento facturable.
URL presinadas en R2, B2, Spaces e Wasabi
Os provedores que implementan a API S3 con SigV4 aceptan o mesmo código: só cambias o endpoint, a rexión e as chaves. Os detalles varían, así que revisa a documentación do provedor:
- Cloudflare R2 admite
GET,HEAD,PUTeDELETEpresinados, cunha caducidade de 1 segundo a 7 días. Non admitePOSTpresinado, e as URL presinadas só funcionan no dominio da API S3 (<ACCOUNT_ID>.r2.cloudflarestorage.com), non en dominios personalizados. Usaregion: 'auto'. - Backblaze B2 indica que a súa API compatible con S3 admite URL presinadas xeradas coa AWS CLI ou cos SDK.
- DigitalOcean Spaces e Wasabi funcionan coas mesmas chamadas do SDK apuntadas aos seus endpoints.
Para os prezos de cada provedor, mira a comparativa de S3, R2, Spaces, Wasabi e B2.
Erros comúns coas URL presinadas e como solucionalos
| Erro | Causa habitual | Solución |
|---|---|---|
SignatureDoesNotMatch | O método, as cabeceiras ou a query difiren do asinado; reloxo desincronizado | Envía o mesmo Content-Type e as mesmas cabeceiras; sincroniza o reloxo con NTP |
AccessDenied “Request has expired” | A URL pasou de X-Amz-Date + X-Amz-Expires | Xera unha URL nova; reduce o tempo entre a sinatura e o uso |
| A URL caduca antes do pedido | Asinada con credenciais temporais (rol, EC2, ECS, Lambda) | Asina con credenciais que duren tanto como teña que durar a ligazón |
ExpiredToken | A sesión STS detrás de X-Amz-Security-Token rematou | Renova as credenciais antes de asinar |
403 AccessDenied nunha URL recente | Quen asina non ten s3:GetObject ou s3:PutObject, ou unha política o denega | Concede o permiso a quen asina; revisa as políticas do bucket |
AuthorizationQueryParametersError | Caducidade superior a 604.800 segundos ou ámbito da credencial mal formado | Mantén X-Amz-Expires en 7 días ou menos; asina na rexión do bucket |
| O navegador bloquea por CORS | Non hai regra de CORS para a túa orixe e o teu método no bucket | Engade unha regra de CORS coa túa orixe, GET/PUT e o ETag exposto |
AccessDenied con HeadersNotSigned | Asinouse Range pero enviouse If-Range sen asinar | Engade If-Range ás cabeceiras asinadas |
Os proxies corporativos que reescriben cabeceiras ou query strings tamén rompen as sinaturas. Se unha URL funciona desde unha rede e non desde outra, proba sen o proxy.
Boas prácticas de seguridade
AWS chama ás URL presinadas bearer tokens e recomenda protexelas. Na práctica:
- Mantenas curtas. Minutos para subidas e ficheiros sensibles, horas para descargas normais. Xera unha nova cando alguén volva necesitar o ficheiro.
- Asina cunha identidade restrinxida. Un usuario ou rol de IAM dedicado que só poida ler (ou só escribir) o prefixo en cuestión limita o dano se se filtra unha ligazón.
- Engade barreiras na política do bucket. A condición
s3:signatureAgerexeita as sinaturas máis antigas dunha idade dada, eaws:SourceIprestrinxe desde que redes se poden usar. - Non as rexistres. As URL presinadas completas nos logs da aplicación, nas analíticas ou no historial dun chat son credenciais que funcionan ata que caducan.
- Ten en conta o que non teñen. Non hai contrasinal, nin contador de descargas, nin revogación por ligazón, así que pon unha aplicación diante cando necesites esas cousas.
Para o lado de compartir en detalle, con opcións que engaden eses controis, mira como compartir ficheiros de S3 sen facer público o bucket.
Como usa Nubbo as URL presinadas
Nubbo está construído sobre este patrón. Cando subes no explorador, o servidor asina un PUT (ou, desde 100 MB, unha subida multipart con tres partes en paralelo) e o teu navegador envía os bytes directamente ao teu bucket. As descargas dun só ficheiro usan URL GET presinadas válidas durante unha hora. As túas access keys gárdanse cifradas con AES-256-GCM e só se usan no servidor para asinar.
As ligazóns para compartir engaden o que lles falta ás URL presinadas sen máis: un contrasinal, unha data de caducidade e un límite de descargas opcionais que Nubbo comproba antes de emitir unha URL de curta duración, e podes desactivar unha ligazón cando queiras. As descargas ZIP de varios ficheiros ármanse no teu navegador, que descarga cada ficheiro directamente do teu bucket: Nubbo nunca ve o contido. Unha única excepción, para ser claros: as miniaturas escríbense no teu propio bucket en .nubbo-thumbs/. Funciona igual en Amazon S3, Cloudflare R2, DigitalOcean Spaces, Wasabi, Backblaze B2 e endpoints públicos compatibles con S3.
Preguntas frecuentes
Que é unha URL presinada en S3?
É unha ligazón HTTPS normal a un obxecto dun bucket privado cunha sinatura engadida á query string. Quen teña a ligazón pode facer a única operación para a que se asinou, normalmente unha descarga (GET) ou unha subida (PUT), ata que caduque. O bucket segue sendo privado e quen usa a ligazón non necesita credenciais de AWS.
Cal é a caducidade máxima dunha URL presinada de S3?
Sete días (604.800 segundos) cando asinas con Signature Version 4 e chaves de longa duración dun usuario de IAM. Unha URL asinada con credenciais temporais, como un rol asumido ou un perfil de instancia EC2, deixa de funcionar cando caducan esas credenciais, aínda que pedises máis tempo. Na consola de S3 podes escoller entre 1 minuto e 12 horas.
Que significan X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders e X-Amz-Signature?
Son os parámetros de autenticación de SigV4. Algorithm é o método de sinatura (AWS4-HMAC-SHA256), Credential é o ID da túa access key máis o ámbito de data, rexión e servizo, Date é cando se asinou a URL, Expires é cantos segundos segue sendo válida, SignedHeaders enumera as cabeceiras cubertas pola sinatura e Signature é a propia sinatura. X-Amz-Security-Token só aparece cando a URL se asinou con credenciais temporais.
Podo revogar unha URL presinada de S3 antes de que caduque?
Non unha por unha. Unha URL presinada só deixa de funcionar antes de tempo cando se revogan, borran ou desactivan as credenciais que a asinaron, ou cando unha política do bucket denega a petición. Se necesitas cancelar ligazóns concretas, pon diante unha aplicación que só entregue URL de curta duración despois das súas propias comprobacións.
As URL presinadas de S3 son seguras?
A sinatura non se pode falsificar nin alterar, e o bucket segue sendo privado. Pero AWS describe as URL presinadas como bearer tokens: calquera que consiga a ligazón pode usala, tantas veces como queira, ata que caduque. Mantén caducidades curtas, usa HTTPS e evita publicar as ligazóns onde se poidan copiar.
As URL presinadas funcionan con Cloudflare R2, Backblaze B2 ou Wasabi?
Os provedores que implementan a API S3 con Signature Version 4 adoitan admitilas, e usas as mesmas chamadas do SDK co endpoint do provedor. Cloudflare R2, por exemplo, admite GET, HEAD, PUT e DELETE presinados cunha caducidade de 1 segundo a 7 días, pero non subidas por formulario POST nin dominios personalizados.
Por que a miña URL presinada devolve SignatureDoesNotMatch?
AWS indica tres causas habituais: un reloxo do ordenador desincronizado, un proxy que cambia as cabeceiras ou a query string, e unha petición cuxo método, cabeceiras ou parámetros difiren dos que se asinaron. Nas subidas, o culpable habitual é enviar un Content-Type distinto do que se usou ao xerar a URL.
Fontes
Os prezos e límites saen das páxinas oficiais de abaixo. Os provedores cámbianos, así que compróbaos antes de decidir.
- Amazon S3: descargar e subir obxectos con URL presinadas
- AWS IAM: métodos de autenticación (parámetros de query de SigV4)
- API de Amazon S3: autenticar peticións con parámetros de query (SigV4)
- Referencia da AWS CLI: s3 presign
- Referencia de Boto3: generate_presigned_url
- AWS SDK for JavaScript v3: @aws-sdk/s3-request-presigner
- Amazon S3: límites das subidas multipart
- AWS SDKs and Tools: protección da integridade dos datos en Amazon S3
- Cloudflare R2: URL presinadas
- Backblaze: a API compatible con S3 de B2 admite URL presinadas?