Artigo

URLs pré-assinados do S3: o que são, como funcionam, limites de expiração e exemplos de código

O que é um URL pré-assinado do S3, o que significa cada parâmetro X-Amz, o limite de 7 dias, código de upload e download (CLI, boto3, JS) e erros comuns.

Um URL pré-assinado do S3 é um link temporário para um objeto de um bucket privado. Leva uma assinatura na query string, por isso quem o tiver pode transferir (ou carregar) esse objeto sem credenciais da AWS até o link expirar, e o bucket nunca se torna público. Cria o URL com as suas próprias chaves, no seu código ou a partir da AWS CLI, e pode durar entre 1 segundo e 7 dias. Este guia explica como funciona, o que significa cada parâmetro X-Amz-*, quanto tempo pode durar, código para transferências e carregamentos e os erros que tem mais probabilidade de encontrar. Os limites vêm da documentação da AWS e da Cloudflare em outubro de 2026.

URLs pré-assinados do S3 num relance

PerguntaResposta
O que permite?Uma operação (GET, PUT, HEAD, DELETE…) sobre um objeto
Expiração máxima7 dias (604.800 segundos) com SigV4 e chaves de utilizador IAM
Expiração com chaves temporáriasTermina quando acaba a sessão da função ou as credenciais STS, o que vier antes
Intervalo na consola do S3De 1 minuto a 12 horas
Expiração predefinida3.600 s na AWS CLI e no boto3, 900 s no presigner de JavaScript v3
Pode ser usado várias vezes?Sim, quantas vezes se quiser até expirar
É possível revogar um só URL?Não, só revogando as credenciais que assinam ou negando-o numa política
Criar um URL faz chamadas ao S3?Não, a assinatura é calculada localmente

Como funciona um URL pré-assinado

  1. O seu código (ou a CLI) recebe o bucket, a chave do objeto, o método HTTP e um tempo de expiração.
  2. O SDK assina esses valores com a sua secret access key usando AWS Signature Version 4. Nesta fase não se envia nada ao S3.
  3. Entrega o URL resultante a alguém: um navegador, um colega, uma aplicação móvel.
  4. Quando o usam, o S3 volta a calcular a assinatura com o segredo que pertence à chave de acesso presente no URL. Se coincidir, se o URL não tiver expirado e se quem assinou puder fazer essa operação, o pedido é aceite.

O URL leva as permissões de quem o assinou. A AWS é explícita neste ponto: se o utilizador ou a função IAM que gerou o URL não puder ler o objeto, o URL devolve 403 por muito válida que seja a assinatura.

O que significam os parâmetros X-Amz

Um URL pré-assinado SigV4 tem este aspeto (com quebras de linha acrescentadas):

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âmetroSignificado
X-Amz-AlgorithmO algoritmo de assinatura: AWS4-HMAC-SHA256 para SigV4
X-Amz-CredentialID da chave de acesso mais o âmbito: <key-id>/<YYYYMMDD>/<region>/s3/aws4_request
X-Amz-DateMomento em que o URL foi assinado, em UTC, yyyyMMddTHHmmssZ
X-Amz-ExpiresQuantos segundos o URL é válido depois de X-Amz-Date, de 1 a 604800
X-Amz-SignedHeadersCabeçalhos abrangidos pela assinatura, sempre pelo menos host
X-Amz-SignatureA assinatura, 64 caracteres hexadecimais
X-Amz-Security-TokenSó aparece quando o URL foi assinado com credenciais temporárias (STS, funções, EC2, ECS, SSO)

Se alterar qualquer parte assinada (a chave, o método, a expiração ou um cabeçalho assinado), a assinatura deixa de coincidir. Repare também no que o URL revela em texto simples: o nome do bucket, a chave completa do objeto e o ID da sua chave de acesso. A chave secreta nunca está no URL.

Quanto tempo dura um URL pré-assinado

O limite depende das credenciais que o assinam:

Assinado comValidade máxima
Chaves de acesso de utilizador IAM (SigV4)7 dias, porque a própria chave de assinatura SigV4 é válida durante 7 dias
Sessão de função IAM (STS AssumeRole)Até a sessão terminar, 1 hora por predefinição
Perfil de instância EC2Até as credenciais da função rodarem, no máximo cerca de 6 horas
Função de tarefa ECSAté as credenciais da tarefa rodarem, normalmente a cada 1 a 6 horas
Credenciais revogadas ou eliminadasDeixa de funcionar de imediato, diga o que disser X-Amz-Expires
Consola do S3Escolhe entre 1 minuto e 12 horas

Isto explica a surpresa mais comum: pede 7 dias a partir de uma função Lambda ou de uma instância EC2 e o link morre ao fim de uma hora ou de poucas horas. O URL só dura tanto quanto as credenciais que estão por trás dele. Se precisa de links longos, assine com um utilizador IAM dedicado que só tenha acesso de leitura a esse bucket.

O S3 verifica a expiração quando um pedido começa. Uma transferência grande que comece um segundo antes do limite termina normalmente, mas uma nova tentativa depois do limite falha com AccessDenied e a mensagem “Request has expired”. Pedir mais de 604.800 segundos é rejeitado logo à partida.

Como gerar um URL pré-assinado

AWS CLI

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

O comando assina um URL de transferência (GET). A predefinição é de 3.600 segundos e o máximo de 604.800. As versões recentes da CLI usam sempre SigV4, por isso garanta que tem uma região configurada. Para URLs de carregamento precisa de um 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,
)

Defina signature_version='s3v4' de forma explícita. A referência do boto3 avisa que, por compatibilidade com versões anteriores, pode assinar com a antiga Signature Version 2 nas regiões que ainda a aceitam, o que descarta silenciosamente alguns parâmetros e cabeçalhos, como Range. ResponseContentDisposition é opcional: faz com que o navegador guarde o ficheiro com um nome limpo em vez de o abrir diretamente.

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 omitir expiresIn, o presigner de JavaScript usa 900 segundos (15 minutos).

URLs pré-assinados de carregamento (PUT)

A mesma ideia funciona no sentido inverso. Assina um PutObject para uma chave e quem tiver o URL pode escrever esse objeto. O ficheiro vai diretamente do dispositivo dessa pessoa para o bucket e o seu servidor nunca toca nos bytes. Se já existir um objeto com essa chave, é substituído.

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

Acrescentar content-type a signableHeaders associa o URL a esse tipo, por isso um carregamento com outro Content-Type é rejeitado. No boto3, o equivalente é generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}), e o cliente tem de enviar o mesmo cabeçalho.

Três coisas a verificar antes de carregar a partir de um navegador:

  • CORS. O bucket precisa de uma regra CORS que permita a sua origem e o método PUT. Para carregamentos multipart, também tem de expor o cabeçalho ETag, porque o navegador tem de o ler em cada parte.
  • Chave fixa. Gere uma chave única no servidor (por exemplo, com um prefixo UUID) para que um visitante não possa substituir o ficheiro de outra pessoa.
  • Checksums. Os SDKs atuais da AWS acrescentam por predefinição um checksum CRC aos carregamentos. Se os carregamentos pré-assinados falharem num fornecedor compatível com S3 com erros de assinatura ou de checksum, defina requestChecksumCalculation: 'WHEN_REQUIRED' no cliente.

O POST pré-assinado é um mecanismo à parte para formulários HTML simples, com uma política que pode limitar o tamanho e o tipo de conteúdo. Nem todos os fornecedores compatíveis com S3 o suportam (o R2 não), por isso o PUT pré-assinado é a opção mais portátil.

Ficheiros grandes: carregamentos multipart pré-assinados

Um único PUT para o S3 está limitado a 5 GB e a AWS sugere multipart a partir de cerca de 100 MB. O multipart divide o ficheiro em até 10.000 partes, cada uma entre 5 MiB e 5 GiB exceto a última, que se carregam de forma independente, em paralelo e com novas tentativas por parte. A pré-assinatura funciona por parte: o servidor inicia o carregamento, assina um URL UploadPart por cada parte, o navegador envia cada bloco e devolve o respetivo ETag, e o servidor conclui o carregamento.

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

Acrescente uma regra de ciclo de vida que cancele os carregamentos multipart incompletos ao fim de alguns dias, ou as partes abandonadas continuam a ocupar armazenamento faturável.

URLs pré-assinados no R2, B2, Spaces e Wasabi

Os fornecedores que implementam a API do S3 com SigV4 aceitam o mesmo código: só muda o endpoint, a região e as chaves. Os detalhes variam, por isso consulte a documentação do fornecedor:

  • O Cloudflare R2 suporta GET, HEAD, PUT e DELETE pré-assinados, com uma expiração entre 1 segundo e 7 dias. Não suporta POST pré-assinado e os URLs pré-assinados só funcionam no domínio da API do S3 (<ACCOUNT_ID>.r2.cloudflarestorage.com), não em domínios personalizados. Use region: 'auto'.
  • A Backblaze indica que a API compatível com S3 do B2 suporta URLs pré-assinados gerados com a AWS CLI ou com os SDKs.
  • O DigitalOcean Spaces e o Wasabi funcionam com as mesmas chamadas do SDK apontadas para os respetivos endpoints.

Para os preços de cada fornecedor, consulte a comparação entre S3, R2, Spaces, Wasabi e B2.

Erros comuns dos URLs pré-assinados e como resolvê-los

ErroCausa habitualSolução
SignatureDoesNotMatchMétodo, cabeçalhos ou query diferentes do que foi assinado; relógio dessincronizadoEnvie o mesmo Content-Type e os mesmos cabeçalhos; sincronize o relógio com NTP
AccessDenied “Request has expired”O URL já passou de X-Amz-Date + X-Amz-ExpiresGere um URL novo; reduza o intervalo entre a assinatura e o uso
O URL expira antes do pedidoAssinado com credenciais temporárias (função, EC2, ECS, Lambda)Assine com credenciais que durem tanto quanto o link precisa
ExpiredTokenA sessão STS por trás de X-Amz-Security-Token terminouRenove as credenciais antes de assinar
403 AccessDenied num URL acabado de gerarQuem assina não tem s3:GetObject ou s3:PutObject, ou uma política nega-oDê a permissão à identidade que assina; reveja as políticas do bucket
AuthorizationQueryParametersErrorExpiração acima de 604.800 segundos ou âmbito de credencial mal formadoMantenha X-Amz-Expires em 7 dias ou menos; assine na região do bucket
Navegador bloqueado por CORSNão há regra CORS para a sua origem e o seu método no bucketAcrescente uma regra CORS com a sua origem, GET/PUT e ETag exposto
AccessDenied com HeadersNotSignedRange foi assinado mas If-Range foi enviado sem assinarAcrescente If-Range aos cabeçalhos assinados

Os proxies empresariais que reescrevem cabeçalhos ou query strings também partem as assinaturas. Se um URL funciona numa rede e não noutra, teste sem o proxy.

Boas práticas de segurança

A AWS chama bearer tokens aos URLs pré-assinados e recomenda protegê-los. Na prática:

  • Mantenha-os curtos. Minutos para carregamentos e ficheiros sensíveis, horas para transferências comuns. Gere um novo quando alguém voltar a precisar do ficheiro.
  • Assine com uma identidade restrita. Um utilizador ou função IAM dedicado que só possa ler (ou só escrever) o prefixo em causa limita os danos se um link for divulgado.
  • Acrescente salvaguardas na política do bucket. A condição s3:signatureAge rejeita assinaturas com mais do que uma determinada idade e aws:SourceIp restringe as redes que as podem usar.
  • Não os registe. URLs pré-assinados completos em registos da aplicação, analítica ou histórico de chat são credenciais válidas até expirarem.
  • Conte com o que lhes falta. Não há palavra-passe, contagem de transferências nem revogação por link, por isso coloque uma aplicação à frente quando precisar disso.

Para a parte da partilha em detalhe, com opções que acrescentam esses controlos, consulte como partilhar ficheiros do S3 sem tornar o bucket público.

Como o Nubbo usa os URLs pré-assinados

O Nubbo assenta neste padrão. Quando carrega no explorador, o servidor assina um PUT (ou, a partir de 100 MB, um carregamento multipart com três partes em paralelo) e o seu navegador envia os bytes diretamente para o seu bucket. As transferências de um só ficheiro usam URLs GET pré-assinados válidos durante uma hora. As suas chaves de acesso são guardadas encriptadas com AES-256-GCM e só são usadas no servidor para assinar.

As ligações de partilha acrescentam o que falta aos URLs pré-assinados simples: uma palavra-passe opcional, uma data de expiração e um limite de transferências que o Nubbo verifica antes de emitir um URL de curta duração, e pode desativar um link a qualquer momento. As transferências ZIP de vários ficheiros são montadas no seu navegador, que transfere cada ficheiro diretamente do seu bucket: o Nubbo nunca vê o conteúdo. Uma exceção honesta: as miniaturas são escritas no seu próprio bucket em .nubbo-thumbs/. Funciona da mesma forma no Amazon S3, no Cloudflare R2, no DigitalOcean Spaces, no Wasabi, no Backblaze B2 e em endpoints públicos compatíveis com S3.

Perguntas frequentes

O que é um URL pré-assinado no S3?

É um link HTTPS normal para um objeto de um bucket privado, com uma assinatura acrescentada à query string. Quem tiver o link pode fazer a única operação para a qual foi assinado, normalmente uma transferência (GET) ou um carregamento (PUT), até expirar. O bucket continua privado e quem usa o link não precisa de credenciais da AWS.

Qual é a expiração máxima de um URL pré-assinado do S3?

Sete dias (604.800 segundos) quando assina com Signature Version 4 e chaves de longa duração de um utilizador IAM. Um URL assinado com credenciais temporárias, como uma função assumida ou um perfil de instância EC2, deixa de funcionar quando essas credenciais expiram, mesmo que tenha pedido mais tempo. Na consola do S3 pode escolher entre 1 minuto e 12 horas.

O que significam X-Amz-Algorithm, X-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amz-SignedHeaders e X-Amz-Signature?

São os parâmetros de autenticação SigV4. Algorithm é o método de assinatura (AWS4-HMAC-SHA256), Credential é o ID da sua chave de acesso com o âmbito de data, região e serviço, Date é o momento em que o URL foi assinado, Expires é o número de segundos durante os quais é válido, SignedHeaders lista os cabeçalhos abrangidos pela assinatura e Signature é a própria assinatura. X-Amz-Security-Token só aparece quando o URL foi assinado com credenciais temporárias.

Posso revogar um URL pré-assinado do S3 antes de expirar?

Não um a um. Um URL pré-assinado só deixa de funcionar antes do tempo quando as credenciais que o assinaram são revogadas, eliminadas ou desativadas, ou quando uma política do bucket nega o pedido. Se precisa de cancelar links individuais, coloque à frente uma aplicação que só entregue URLs de curta duração depois das suas próprias verificações.

Os URLs pré-assinados do S3 são seguros?

A assinatura não pode ser falsificada nem alterada e o bucket continua privado. Mas a AWS descreve os URLs pré-assinados como bearer tokens: qualquer pessoa que obtenha o link pode usá-lo, quantas vezes quiser, até expirar. Mantenha tempos de expiração curtos, use HTTPS e evite publicar os links onde possam ser copiados.

Os URLs pré-assinados funcionam com Cloudflare R2, Backblaze B2 ou Wasabi?

Os fornecedores que implementam a API do S3 com Signature Version 4 suportam-nos em geral, e usa as mesmas chamadas do SDK com o endpoint do fornecedor. O Cloudflare R2, por exemplo, suporta GET, HEAD, PUT e DELETE pré-assinados com uma expiração entre 1 segundo e 7 dias, mas não carregamentos por formulário POST nem domínios personalizados.

Porque é que o meu URL pré-assinado devolve SignatureDoesNotMatch?

A AWS indica três causas comuns: um relógio do computador dessincronizado, um proxy que altera os cabeçalhos ou a query string e um pedido cujo método, cabeçalhos ou parâmetros são diferentes dos que foram assinados. Nos carregamentos, o culpado habitual é enviar um Content-Type diferente do usado ao gerar o URL.

Fontes

Os preços e limites vêm das páginas oficiais abaixo. Os fornecedores alteram-nos, por isso confirme antes de se comprometer.