# What Is a Presigned URL? — Signed, Time-Boxed Access to a Stored Object

Canonical: https://commareports.com/glossary/presigned-url
Published: 2026-09-11

> A presigned URL grants temporary access to a private object without credentials. How the signature works, the real expiry ceilings, and why it is a poor fit for a report someone must read.

# Presigned URL

**A presigned URL is a link to a private stored object carrying a
cryptographic signature and an expiry in its query string.** Whoever
holds it can read the object until it expires, with no credentials of
their own.

```
https://bucket.s3.amazonaws.com/reports/index.html
  ?X-Amz-Algorithm=AWS4-HMAC-SHA256
  &X-Amz-Credential=AKIA…%2F20260911%2Fus-east-1%2Fs3%2Faws4_request
  &X-Amz-Date=20260911T090000Z
  &X-Amz-Expires=3600
  &X-Amz-SignedHeaders=host
  &X-Amz-Signature=8f2c…
```

The signature is an HMAC over the request the URL describes — bucket,
key, method, expiry. The storage service recomputes it and compares. S3
popularised the pattern; GCS signed URLs and Azure SAS tokens are the
same idea.

## The expiry ceilings that catch people

- **Seven days** is the maximum requestable lifetime under AWS
  Signature Version 4.
- **The signing credentials' own lifetime wins.** A URL signed with a
  role's temporary credentials dies when that session does — frequently
  one hour — no matter what `X-Amz-Expires` said.
- Which is why "I set it to seven days and it died at lunch" is a
  standing support question: the expiry was never the binding constraint.

## They are bearer tokens

Possession is authorisation. Consequences worth stating plainly:

- Forwarding the link forwards the access. A URL pasted into a channel
  grants it to everyone in the channel, and to anything that indexes it.
- There is no per-recipient revocation. Killing one link means rotating
  the signing key or moving the object.
- There is no per-reader access log. The object access log records
  requests, not identities.

## Why a report is the wrong payload

An [HTML report](/glossary/html-report) is a directory, not an object.
Presigning it means presigning every asset — because the entry page loads
under one signature and then requests stylesheets and data files that
carry none, and they 403. Workarounds exist (a bucket policy, CloudFront
with a signed cookie, an origin access identity), and each one is more
infrastructure between a person and a document.

Then the delivery problems remain: the link expires while the reader is
on holiday, the [MIME type](/glossary/mime-type) may make the browser
download rather than render it
([S3 downloads instead of opening](/fix/s3-html-downloads-instead-of-opening)),
and nobody can comment on what they read.

## The alternative

A permanent URL with real access control: unlisted by default, or
restricted to named people or a domain, revocable at any time,
with a per-reader view record, and able to hold threads anchored to
specific content in the report. No expiry to set, and no second
signature for assets — they are part of the report.

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited
revision history.

**[Publish a report without presigning it →](https://commareports.com/)**

### Related

- [S3 static hosting alternatives](/alternatives/s3-static-hosting-alternatives) · [Static site hosting](/glossary/static-site-hosting)
- [MIME type](/glossary/mime-type) · [Password-protect an HTML report](/password-protect-html-report)
- [S3 downloads instead of opening](/fix/s3-html-downloads-instead-of-opening) · [Glossary](/glossary)
