Keep a presigned artifact URL to minutes, not days. It works like a bearer token: anyone holding it can use it until it expires. AWS allows up to 7 days with long term keys, but a short life and temporary credentials shrink the damage if the link leaks.
What a presigned URL is
AWS describes a presigned URL as time limited access to an object without changing the bucket policy. The credentials it uses are those of the principal who generated it. You can reuse the URL many times until it expires. AWS says plainly that presigned URLs are bearer tokens that grant access to whoever holds them, so protect them appropriately.
That is why they leak in surprising ways: pasted into chat, saved in build logs, stored in an issue tracker. The link needs no login.
Expiry limits
| How it was created | Maximum life |
|---|---|
| S3 console | 1 minute to 12 hours |
| CLI or SDK | Up to 7 days |
| IAM user keys with Signature Version 4 | Up to 7 days |
| IAM role session credentials | Ends when the role session ends |
| EC2 instance role credentials | The credential duration, typically 6 hours |
AWS's note matters most for CI: a URL made with temporary credentials expires when those credentials expire, even if you asked for a later time. So a pipeline that signs with a one hour role session produces links that die within the hour. That is a feature.
The link also stops if the credential used to create it is revoked, deleted or deactivated. If you deactivate an old key, every URL signed with it stops working.
Choosing a lifetime
- Ask who downloads the file and how fast. A deploy target in the same pipeline needs minutes.
- For humans downloading a release, 15 minutes to an hour is usually enough, since they can request a fresh link.
- Sign with a role session, not an IAM user key, so there is a hard upper bound.
- Avoid putting links in places that are archived, such as release notes. Use an authenticated download page instead.
- If a build log might contain a URL, shorten the lifetime and mask the value in output.
Note that S3 checks the expiry at the time of the request. A big download started just before expiry continues, but a restart after expiry fails.
Extra controls
AWS shows a bucket policy that denies presigned requests when the signature is older than a set number of minutes, using the s3:signatureAge condition. That gives you a ceiling independent of whatever lifetime a developer chose. A network path restriction is another option: require requests to come from a given IP range or a VPC endpoint, which also applies to presigned requests.
The capability of the link is also bounded by the signer. A role with read access to one prefix creates links that read only that prefix. Another reason to keep the signing role narrow.
What to do when one leaks
Revoke the credential that signed it if that is feasible, which invalidates the link. For role session credentials, the link ends when the session does, so a short session is your best protection. Then rotate anything the artifact contained.
Quick check
Is a presigned URL safe to share in chat?
Treat it like a password for that object. AWS calls presigned URLs bearer tokens, so anyone with the link can use it until expiry.
Why did my link die before the time I set?
It was likely signed with temporary credentials. AWS says the URL expires when those credentials expire.
Safeguard does not manage your cloud accounts. It scans your code and dependencies, applies pull request policies and gates, and automates compliance evidence. Free scan: gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.