Stable links
How one link keeps pointing at the right bytes while you replace a file, how to pin a specific revision, and what caches do in between.
On this page
Every file in SteadyLink has one permanent delivery link. When you publish a new version, the link stays the same and starts serving the new bytes. This page explains what that link points at, how to pin an older revision on purpose, how caching affects what people see after a change, and the few situations where a link can stop working.
Identity and bytes are separate#
A file (the API calls it an asset) has an ID that is assigned once, when the file is first uploaded, and never changes. The bytes live in revisions. Each upload to the same file adds a revision, and the file has exactly one current revision at a time.
The stable link is built from the file ID, not from the bytes or the filename:
https://cdn.steadylink.io/a/3f2a9c1e-7b4d-4e8a-9c61-2d5f0a8b7e13Because the link names the identity, anything that changes the bytes leaves the link alone: replacing the file, restoring an older revision, or scheduling a revision to publish later.
What creates a new revision instead of a new file#
Uploading a file to a bucket path that already holds a file adds a revision to that existing file. Uploading press/logo.png twice gives you one file with revisions 1 and 2, not two files. The same applies to replacements from the dashboard, the API, the SDKs, the CLI, and approved replacement requests.
Renaming or moving a file inside its bucket keeps its ID, its revisions, and its link.
For a task-focused walkthrough, see how to replace an image without changing its URL or how to replace any file and keep the same URL.
The two kinds of link#
| Link | Serves | Cache lifetime | Use it when |
|---|---|---|---|
/a/{asset_id} | The current revision, whatever it is right now | 5 minutes | You want people to always get the latest version: websites, emails, QR codes, printed material |
/a/{asset_id}?v=3 | Revision 3, always | 1 year, immutable | A consumer must not change without a deliberate update: a legal document someone signed, a build artifact, a reproducible test fixture |
The revision number in ?v= is the versionNumber from the file's revision history. A pinned link works for as long as that revision is retained. Delete the revision and the pinned link returns 404.
Image transformations work on both kinds of link. /a/{asset_id}?w=1200&fm=webp resizes the current revision; /a/{asset_id}?v=3&w=1200&fm=webp resizes revision 3. See Image transformations.
Caching#
Delivery responses carry Cache-Control headers that browsers and CDNs follow:
| Response | Cache-Control |
|---|---|
| Stable link, public file | public, max-age=300, stale-while-revalidate=3600, stale-if-error=3600 |
Pinned link (?v=), public file | public, max-age=31536000, immutable |
| Private file (any link with a valid token) | private, no-store, max-age=0 |
What this means in practice:
- After a replacement or a restore, the SteadyLink API serves the new current revision immediately. A browser or CDN that already holds the previous response can keep showing it for up to five minutes, and briefly longer while it revalidates in the background.
- Pinned responses never change. They are cached for a year and marked immutable, so a cache never asks again. That is safe because revision bytes are never edited in place.
- Private files are never stored in shared caches. Every request reaches SteadyLink and the token is checked each time, which is what makes revoking a signed link take effect at once.
Replacing, restoring, and rolling back#
Replacing uploads new bytes. When the upload finishes, SteadyLink stores them as a new revision and makes it current. The previous revision is kept in the revision history.
Restoring (the API calls it promoting) makes an older revision current again. No bytes are copied and no new revision is created; the current pointer moves. A rollback is the same operation. When a public file's revision is promoted, SteadyLink also asks the CDN to fetch the new current version so the first visitor does not pay for a cold cache.
Only revisions that passed the malware scan can be restored. See Revisions.
The scan window after a replacement#
When malware scanning is on (it is on in production), a new revision becomes current as soon as the upload is finalized, and it is scanned right after. Until the scan finishes, the stable link responds with 423 Locked instead of serving unscanned bytes. Scans usually take seconds; very large files take longer.
What happens when the replacement is infected depends on when the malware is found. The infected bytes are never served in either case.
- Caught before commit. Small files, files whose hash is already known, and bytes with a cached verdict are scanned inline. An infected file is rejected with
400(Upload blocked by malware scan), no revision is created, and the previous revision keeps serving. - Caught by the background scan. Otherwise the new revision is already current when the scan finds malware. It is marked infected and the file is left with no current revision, so the stable link returns
404until you restore an earlier clean revision.
If a gap of a few seconds is not acceptable, publish the change on a schedule instead: upload or approve the new version with a future publish time, and SteadyLink switches the link to it only at that time. See Replacement requests.
What changes and what never changes#
These stay the same for the life of the file:
- The file ID and the stable link
/a/{asset_id}. - The bytes behind any pinned link
?v=N, while that revision exists. - Revision numbers already assigned to retained revisions.
These can change at any time:
- The bytes, size, and content type served by the stable link.
- Image dimensions, and so any transformed variant of the stable link.
- Whether the link opens for everyone or needs a token, when someone switches the file between public and private. See Private links.
When a link stops working#
A stable link only breaks when the identity goes away or delivery is refused on purpose:
| Situation | Response |
|---|---|
| The file is private and the request has no valid token | 403 |
| The current revision is still being scanned | 423 |
| The current revision was found to be infected | 404 until a clean revision is restored |
| The last remaining revision was deleted, which deletes the file | 404 |
| The bucket holding the file was deleted | 404 |
| A pinned link names a revision that was deleted | 404 |
Deleting a bucket or a file is immediate and has no undo. Download anything you want to keep first.