Artigo

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

O que é uma URL pré-assinada 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.

Uma URL pré-assinada do S3 é um link temporário para um objeto de um bucket privado. Ela leva uma assinatura na query string, então quem a tiver pode baixar (ou enviar) esse objeto sem credenciais da AWS até o link expirar, e o bucket nunca se torna público. Você cria a URL com as suas próprias chaves, no seu código ou pela AWS CLI, e ela pode durar de 1 segundo a 7 dias. Este guia explica como ela funciona, o que significa cada parâmetro X-Amz-*, quanto tempo ela pode durar, código para downloads e uploads e os erros que você tem mais chance de encontrar. Os limites vêm da documentação da AWS e da Cloudflare em outubro de 2026.

URLs pré-assinadas do S3 em resumo

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

Como funciona uma URL pré-assinada

  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. Nessa etapa nada é enviado ao S3.
  3. Você entrega a URL resultante a alguém: um navegador, um colega, um app para celular.
  4. Quando ela é usada, o S3 recalcula a assinatura com o segredo que pertence à chave de acesso presente na URL. Se coincidir, se a URL não tiver expirado e se quem assinou puder fazer essa operação, a requisição é aceita.

A URL carrega as permissões de quem a assinou. A AWS é explícita nesse ponto: se o usuário ou a função IAM que gerou a URL não puder ler o objeto, a URL retorna 403 por mais válida que seja a assinatura.

O que significam os parâmetros X-Amz

Uma URL pré-assinada SigV4 tem esta cara (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 escopo: <key-id>/<YYYYMMDD>/<region>/s3/aws4_request
X-Amz-DateMomento em que a URL foi assinada, em UTC, yyyyMMddTHHmmssZ
X-Amz-ExpiresPor quantos segundos a URL é válida depois de X-Amz-Date, de 1 a 604800
X-Amz-SignedHeadersCabeçalhos cobertos pela assinatura, sempre pelo menos host
X-Amz-SignatureA assinatura, 64 caracteres hexadecimais
X-Amz-Security-TokenSó aparece quando a URL foi assinada com credenciais temporárias (STS, funções, EC2, ECS, SSO)

Se você 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 a URL revela em texto puro: o nome do bucket, a chave completa do objeto e o ID da sua chave de acesso. A chave secreta nunca está na URL.

Quanto tempo dura uma URL pré-assinada

O limite depende das credenciais que assinam:

Assinada comValidade máxima
Chaves de acesso de usuário IAM (SigV4)7 dias, porque a própria chave de assinatura SigV4 é válida por 7 dias
Sessão de função IAM (STS AssumeRole)Até a sessão terminar, 1 hora por padrão
Perfil de instância EC2Até as credenciais da função serem rotacionadas, no máximo cerca de 6 horas
Função de tarefa ECSAté as credenciais da tarefa serem rotacionadas, normalmente a cada 1 a 6 horas
Credenciais revogadas ou excluídasPara de funcionar na hora, diga o que disser X-Amz-Expires
Console do S3Você escolhe entre 1 minuto e 12 horas

Isso explica a surpresa mais comum: você pede 7 dias a partir de uma função Lambda ou de uma instância EC2 e o link morre depois de uma hora ou de poucas horas. A URL só dura tanto quanto as credenciais por trás dela. Se você precisa de links longos, assine com um usuário IAM dedicado que só tenha acesso de leitura a esse bucket.

O S3 verifica a expiração quando uma requisição começa. Um download 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 de cara.

Como gerar uma URL pré-assinada

AWS CLI

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

O comando assina uma URL de download (GET). O padrão é de 3.600 segundos e o máximo de 604.800. As versões recentes da CLI usam sempre SigV4, então garanta que você tem uma região configurada. Para URLs de upload você 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, ele 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 o navegador salvar o arquivo com um nome limpo em vez de abri-lo 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 você omitir expiresIn, o presigner de JavaScript usa 900 segundos (15 minutos).

URLs pré-assinadas de upload (PUT)

A mesma ideia funciona no sentido inverso. Você assina um PutObject para uma chave e quem tiver a URL pode gravar esse objeto. O arquivo vai direto do dispositivo dessa pessoa para o bucket e o seu servidor nunca toca nos bytes. Se já existir um objeto com essa chave, ele é 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 vincula a URL a esse tipo, então um upload com outro Content-Type é rejeitado. No boto3, o equivalente é generate_presigned_url('put_object', Params={'Bucket': ..., 'Key': ..., 'ContentType': 'application/pdf'}), e o cliente precisa enviar o mesmo cabeçalho.

Três coisas a conferir antes de fazer upload a partir de um navegador:

  • CORS. O bucket precisa de uma regra CORS que permita a sua origem e o método PUT. Para uploads multipart, ela também precisa expor o cabeçalho ETag, porque o navegador tem que lê-lo 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 arquivo de outra pessoa.
  • Checksums. Os SDKs atuais da AWS acrescentam por padrão um checksum CRC aos uploads. Se os uploads pré-assinados falharem em um provedor 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 provedores compatíveis com S3 o suportam (o R2 não), então o PUT pré-assinado é a opção mais portátil.

Arquivos grandes: uploads 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 arquivo em até 10.000 partes, cada uma entre 5 MiB e 5 GiB exceto a última, que são enviadas de forma independente, em paralelo e com novas tentativas por parte. A pré-assinatura funciona por parte: o servidor inicia o upload, assina uma URL UploadPart para cada parte, o navegador envia cada bloco e devolve o respectivo ETag, e o servidor conclui o upload.

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 uploads multipart incompletos depois de alguns dias, ou as partes abandonadas continuam ocupando armazenamento cobrado.

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

Os provedores que implementam a API do S3 com SigV4 aceitam o mesmo código: só mudam o endpoint, a região e as chaves. Os detalhes variam, então consulte a documentação do provedor:

  • O Cloudflare R2 suporta GET, HEAD, PUT e DELETE pré-assinados, com uma expiração entre 1 segundo e 7 dias. Ele não suporta POST pré-assinado e as URLs pré-assinadas 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 informa que a API compatível com S3 do B2 suporta URLs pré-assinadas geradas com a AWS CLI ou com os SDKs.
  • O DigitalOcean Spaces e o Wasabi funcionam com as mesmas chamadas do SDK apontadas para os respectivos endpoints.

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

Erros comuns das URLs pré-assinadas e como resolvê-los

ErroCausa mais comumSoluçã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”A URL já passou de X-Amz-Date + X-Amz-ExpiresGere uma URL nova; reduza o intervalo entre a assinatura e o uso
A URL expira antes da requisiçãoAssinada 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 em uma URL recém-geradaQuem assina não tem s3:GetObject ou s3:PutObject, ou uma política negaDê a permissão à identidade que assina; revise as políticas do bucket
AuthorizationQueryParametersErrorExpiração acima de 604.800 segundos ou escopo de credencial malformadoMantenha 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 assinaturaAcrescente If-Range aos cabeçalhos assinados

Os proxies corporativos que reescrevem cabeçalhos ou query strings também quebram as assinaturas. Se uma URL funciona em uma rede e não em outra, teste sem o proxy.

Boas práticas de segurança

A AWS chama as URLs pré-assinadas de bearer tokens e recomenda protegê-las. Na prática:

  • Mantenha-as curtas. Minutos para uploads e arquivos sensíveis, horas para downloads comuns. Gere uma nova quando alguém precisar do arquivo de novo.
  • Assine com uma identidade restrita. Um usuário ou função IAM dedicado que só possa ler (ou só gravar) o prefixo em questão limita o estrago se um link vazar.
  • Acrescente salvaguardas na política do bucket. A condição s3:signatureAge rejeita assinaturas mais antigas do que uma determinada idade e aws:SourceIp restringe as redes que podem usá-las.
  • Não as registre em logs. URLs pré-assinadas completas em logs do app, analytics ou histórico de chat são credenciais válidas até expirarem.
  • Conte com o que falta a elas. Não há senha, contagem de downloads nem revogação por link, então coloque um app na frente quando precisar disso.

Para a parte do compartilhamento em detalhe, com opções que acrescentam esses controles, confira como compartilhar arquivos do S3 sem tornar o bucket público.

Como o Nubbo usa as URLs pré-assinadas

O Nubbo se apoia nesse padrão. Quando você faz upload no explorador, o servidor assina um PUT (ou, a partir de 100 MB, um upload multipart com três partes em paralelo) e o seu navegador envia os bytes diretamente para o seu bucket. Os downloads de um só arquivo usam URLs GET pré-assinadas válidas por uma hora. As suas chaves de acesso são armazenadas criptografadas com AES-256-GCM e só são usadas no servidor para assinar.

Os links de compartilhamento acrescentam o que falta às URLs pré-assinadas simples: uma senha opcional, uma data de expiração e um limite de downloads que o Nubbo verifica antes de emitir uma URL de curta duração, e você pode desativar um link a qualquer momento. Os downloads ZIP de vários arquivos são montados no seu navegador, que baixa cada arquivo diretamente do seu bucket: o Nubbo nunca vê o conteúdo. Uma exceção honesta: as miniaturas são gravadas no seu próprio bucket em .nubbo-thumbs/. Funciona do mesmo jeito 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 é uma URL pré-assinada 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 ele foi assinado, normalmente um download (GET) ou um upload (PUT), até ele expirar. O bucket continua privado e quem usa o link não precisa de credenciais da AWS.

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

Sete dias (604.800 segundos) quando você assina com Signature Version 4 e chaves de longa duração de um usuário IAM. Uma URL assinada com credenciais temporárias, como uma função assumida ou um perfil de instância EC2, para de funcionar quando essas credenciais expiram, mesmo que você tenha pedido mais tempo. No console do S3 você 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 escopo de data, região e serviço, Date é o momento em que a URL foi assinada, Expires é o número de segundos durante os quais ela é válida, SignedHeaders lista os cabeçalhos cobertos pela assinatura e Signature é a própria assinatura. X-Amz-Security-Token só aparece quando a URL foi assinada com credenciais temporárias.

Posso revogar uma URL pré-assinada do S3 antes de ela expirar?

Não uma por uma. Uma URL pré-assinada só para de funcionar antes do tempo quando as credenciais que a assinaram são revogadas, excluídas ou desativadas, ou quando uma política do bucket nega a requisição. Se você precisa cancelar links individuais, coloque na frente um app que só entregue URLs de curta duração depois das suas próprias verificações.

As URLs pré-assinadas do S3 são seguras?

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

As URLs pré-assinadas funcionam com Cloudflare R2, Backblaze B2 ou Wasabi?

Os provedores que implementam a API do S3 com Signature Version 4 em geral as suportam, e você usa as mesmas chamadas do SDK com o endpoint do provedor. 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 uploads por formulário POST nem domínios personalizados.

Por que minha URL pré-assinada retorna SignatureDoesNotMatch?

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

Fontes

Os preços e limites vêm das páginas oficiais abaixo. Os provedores mudam esses valores, então confirme antes de fechar qualquer compromisso.