OneWebDesk

Signed URL Expiry Calculator

Compute signed URL expiry considering duration and clock skew.

CDNs and object storage such as CloudFront, S3, and Cloudflare hand out signed URLs (or presigned URLs) that embed an expiry time to grant temporary access. Enter a start time and a validity duration, and this calculator converts it into the exact expiry as a UTC ISO 8601 string, a local time, and a Unix epoch (seconds) all at once.

When the signer's clock and the verifier's clock drift apart, a URL that should still be valid can be rejected as expired, or one that should be dead can linger. Provide a clock skew allowance and the tool also computes the effective expiry you can safely rely on. Everything runs in your browser; no value is ever sent anywhere.

How signed URL expiry is computed

The formula is simple: expiry = start time + duration. Most signing schemes encode the expiry as an absolute time (Unix epoch seconds) in the URL. CloudFront uses the policy DateLessThan value; an S3 presigned URL uses X-Amz-Expires (seconds) relative to when it was signed.

  • Start time: usually "now" when you sign the URL, but you can pre-issue with a future start.
  • Duration: entered in seconds, minutes, hours, or days. S3 presigned URLs cap at 7 days (604800s).
  • Expiry: after this, the gateway rejects the request with 403.

Always account for clock skew

The clock that verifies the signature is never perfectly in sync with the one that created the URL. If the verifier's clock runs slightly fast, a URL that the signer still considers valid may already look expired to the verifier. So treat the effective expiry conservatively.

  • Effective expiry = expiry − skew allowance. Design requests to arrive before this moment.
  • Even with NTP, a few seconds of drift is common. A 1–5 second buffer is a safe starting point.
  • Conversely, if your signature has a future start (NotBefore), a slow verifier clock may reject it as "not yet valid," so add the same kind of buffer there too.

Operational tips

  1. Keep expiries short. If a URL leaks, a small window limits the damage.
  2. For long downloads/uploads, the duration must comfortably exceed the full transfer time (whether expiry is checked only at connection start or continuously varies by service).
  3. Store times as UTC epoch in logs and databases; convert to local time only for display. To turn a stored epoch back into a human-readable time, the log timestamp converter is handy.

To also design CDN cache freshness (TTL) and background-refresh policy, see the stale-while-revalidate planner.

Expiry parameters and maximum validity by provider

Each service expresses expiry with a different query-string or policy field, and they cap the maximum validity differently. Some encode expiry as an absolute time (epoch), others as a relative duration (seconds) measured from when the URL was signed — which is an easy thing to mix up.

Provider / schemeExpiry fieldMeaningMax validity
S3 presigned (SigV4)X-Amz-ExpiresSeconds relative to X-Amz-Date (sign time)7 days (604800s)
CloudFront canned policyExpiresAbsolute epoch seconds (DateLessThan)No cap (you set it)
CloudFront custom policyPolicy (base64 JSON)DateLessThan/DateGreaterThan absolute epochNo cap (you set it)
Cloudflare signed URLexp (+ verify)Absolute epoch secondsNo cap (you set it)
GCS signed URL (V4)X-Goog-ExpiresSeconds relative to X-Goog-Date7 days (604800s)
Azure Blob SASse (+ st)ISO 8601 absolute times (start/expiry)No cap (you set it)

This calculator derives expiry from "start + duration" and shows both the epoch and the UTC ISO string, so whichever style your provider wants — a relative duration (X-Amz-Expires) or an absolute epoch (Expires, exp) — you can copy the right value directly.

A worked example

Say you enter a start time of 2026-06-27T09:00:00Z (epoch 1782032400), a duration of 2 hours, and a clock skew allowance of 5 seconds. The results are:

  • Expiry (UTC): 2026-06-27T11:00:00Z — start epoch 1782032400 + 7200s = 1782039600
  • Effective expiry (conservative): 1782039600 − 5 = 17820395952026-06-27T10:59:55Z
  • What you put in S3: X-Amz-Expires=7200 (the duration in seconds). For CloudFront/Cloudflare, Expires=1782039600 or exp=1782039600 (the expiry epoch).

So for the very same expiry, the number you paste might be 7200 or 1782039600 depending on the provider. The calculator's "duration (seconds)" maps to the former, its "expiry epoch" to the latter.

Common pitfall

  • Milliseconds vs. seconds: JavaScript's Date.now() returns milliseconds, but most signing libraries expect epoch seconds. Forgetting to divide by 1000 pushes the expiry ~50,000 years out, producing an effectively immortal URL.
  • Putting a relative duration in an absolute-epoch field: an expiry epoch (e.g. 1782039600) in X-Amz-Expires yields a "50,000-year URL," while a 7200 in Expires is read as 1970 and rejects instantly.
  • Mistaking the local time for the epoch: the on-screen local time is display only. Always sign with the UTC epoch or the UTC ISO value, never the localized string.

Frequently asked questions

What happens if I leave the start time empty?
Click the Now button or type a datetime to set the start. With nothing set, no result is shown. Since URLs are usually issued relative to now, press Now to begin.
What is the maximum validity for an S3 presigned URL?
Under SigV4 the maximum is 7 days (604800 seconds). Longer access is also bounded by the lifetime of any temporary IAM credentials, so consider a different auth method if you need more.
What clock skew allowance should I use?
For well-synced (NTP) servers, 1–5 seconds is often enough. For mobile or embedded clients whose clocks may be unreliable, some teams extend it to 30–60 seconds.
Are my times or URLs sent to a server?
No. Everything is computed locally with the browser's JavaScript Date object; nothing leaves your device.
What time zone does the local time use?
It follows this browser/device's time zone setting. The UTC ISO value and the epoch are absolute and time-zone independent, so use the epoch when comparing across systems.

Related tools