Skip to main content

Signed URLs

Generate time-limited URLs for private file access and upload workflows.

When to Use Signed URLs

By default, files in a bucket with a read rule that returns true are publicly accessible via bucket.getUrl(). But for private files — where the bucket's read rule requires authentication — clients need a way to access files without sending auth headers (e.g., in <img src="..."> tags, PDF viewers, or sharing links with external users).

Signed URLs solve this by embedding a time-limited token directly in the URL:

Regular URL:  https://project.edgebase.dev/api/storage/private/report.pdf      ← 403 Forbidden
Signed URL: https://project.edgebase.dev/api/storage/private/report.pdf?token=eyJ... ← ✅ Works for 1 hour
ScenarioUse
Display a private image in <img>Signed download URL
Share a temporary download link with someoneSigned download URL (set a short expiry)
Let the client upload a large file directly to R2Signed upload URL (bypasses Worker memory limits)
Show a public file in <img>bucket.getUrl() — no signing needed
Access Rule Check

Signed download URLs check the read rule at URL creation time. Signed upload URLs check the write rule at URL creation time. After that, anyone with the URL can access the file until it expires.


Signed Download URL

Create a temporary URL for downloading a private file (default expiry: 1 hour):

const bucket = client.storage.bucket('private');

const url = await bucket.createSignedUrl('report.pdf', {
expiresIn: '1h', // default: 1h
});
// url -> "https://your-project.edgebase.dev/api/storage/private/report.pdf?token=..."

Duration Format

ValueDuration
'30s'30 seconds
'10m'10 minutes
'1h'1 hour (default)
'7d'7 days

Batch Signed URLs

Create signed URLs for multiple files in a single request:

const bucket = client.storage.bucket('private');

const results = await bucket.createSignedUrls(
['report-1.pdf', 'report-2.pdf', 'report-3.pdf'],
{ expiresIn: '1h' },
);
// [
// { key: 'report-1.pdf', url: '...?token=...', expiresAt: '2026-03-01T...' },
// { key: 'report-2.pdf', url: '...?token=...', expiresAt: '2026-03-01T...' },
// { key: 'report-3.pdf', url: '...?token=...', expiresAt: '2026-03-01T...' },
// ]
info

Non-existent files are silently skipped in the response. Maximum 100 keys per request.

Signed Upload URL

Create a temporary upload URL (default expiry: 30 minutes):

const bucket = client.storage.bucket('uploads');

const signed = await bucket.createSignedUploadUrl('large-file.zip', {
expiresIn: '10m', // default: 30m
});

const formData = new FormData();
formData.append('file', file, 'large-file.zip');
formData.append('key', 'large-file.zip');

await fetch(signed.url, {
method: 'POST',
body: formData,
});
info

Signed upload URLs validate write rules when the URL is created.

REST API

EndpointDescription
POST /api/storage/:bucket/signed-urlCreate signed download URL
POST /api/storage/:bucket/signed-urlsBatch create signed download URLs
POST /api/storage/:bucket/signed-upload-urlCreate signed upload URL