url
Return a URL to fetch a key - a signed GetObject where the adapter can sign, or a direct CDN/public URL where one is configured.
files.url(key, options?)
Returns a URL the caller can use to fetch key. Every adapter returns the most direct URL it can produce. Signing adapters (S3 and the S3-compatible catalog — R2 over HTTP plus every regional / budget / decentralised wrapper — alongside Google Cloud Storage, Azure with shared key, Supabase, UploadThing in private mode, and the R2 binding when HTTP credentials are also configured) sign a read URL - defaulting to a 1-hour expiry, override per-call via { expiresIn } or per-adapter via defaultUrlExpiresIn. If the adapter is constructed with a publicBaseUrl (CDN, custom domain, r2.dev, Bunny Pull Zone) or UploadThing’s public-read ACL, that wins and the URL is built without signing.
Three configurations have no URL primitive and throw: Vercel Blob in access: "private" mode, an R2 Workers binding without either publicBaseUrl or HTTP credentials, and Bunny Storage without publicBaseUrl because the Storage API URL requires an AccessKey header.
// One call, every adapter. S3 and the S3-compatible catalog (R2 over HTTP
// plus every regional / budget / decentralised wrapper) sign a GetObject (1h
// default, override with { expiresIn }); Google Cloud Storage and Azure sign
// a read URL natively with the same default; Supabase signs via createSignedUrl
// (or returns the public URL when constructed with public:true); Vercel Blob
// (public), UploadThing (public-read), and Bunny Storage with publicBaseUrl
// return their CDN URLs. If you configured `publicBaseUrl` on the adapter, that
// wins and signing is skipped.
const url = await files.url("avatars/abc.png");
const short = await files.url("avatars/abc.png", { expiresIn: 60 });
// Force download (defeat stored XSS from user-uploaded HTML/SVG).
// Forces signing even if `publicBaseUrl` is configured - a permanent
// CDN URL has no signature to bind the override into, and silently
// dropping a security ask would be a regression.
const safe = await files.url("avatars/abc.png", {
responseContentDisposition: "attachment",
});
Options
expiresIn?number
Override the adapter's default URL expiry, in seconds. **Honored** by adapters that sign (S3, Cloudflare R2 over HTTP, MinIO, DigitalOcean Spaces, Storj, Hetzner, Akamai, Backblaze B2, Wasabi, Tigris, and the R2 binding when HTTP credentials are also configured) — those adapters return a presigned URL that expires after `expiresIn` seconds. **Ignored** by Vercel Blob (public): the underlying CDN URL has no expiry, and the adapter returns it unchanged. If you need expiring URLs there, you'll need a different provider — Vercel Blob has no signing primitive. **N/A** for adapters where `url()` throws (Vercel Blob private; the R2 binding without `publicBaseUrl` and without HTTP credentials).
numberresponseContentDisposition?string
Override the `Content-Disposition` header on the response. **Strongly recommended** for buckets that contain user-uploaded content. Without this override, the browser uses the stored Content-Type to decide whether to render or download, which means a user-uploaded `.html` (or SVG with embedded scripts) will execute inline at your bucket's origin — stored XSS in the trust context of your domain. Pass `"attachment"` (or `'attachment; filename="..."'`) to force a download. **Forces the signing path.** On signing adapters (S3, R2 HTTP, MinIO, DigitalOcean Spaces, Storj, Hetzner, Akamai, Backblaze B2, Wasabi, Tigris, R2 hybrid), passing this option always returns a presigned URL — even when `publicBaseUrl` is configured, because a permanent CDN URL has no signature in which to bind the override. If `publicBaseUrl` was the deliberate choice and you also need the security override, the override wins (it's the safe default). **Throws** on Vercel Blob (no Content-Disposition primitive) and on the R2 binding without HTTP credentials (can't sign). These cases fail loudly rather than silently dropping the security ask.
stringsignal?AbortSignal
Abort the operation when this signal is aborted. When both constructor and per-call signals are provided, either one can abort the call.
AbortSignaltimeout?number
Overall timeout in milliseconds, applied to each attempt. A timeout aborts the operation and is not retried. `0` or a negative value disables timeout handling.
numberretries?RetryOptions
Retry provider failures. A number is treated as `{ max: number }`.
RetryOptions