Sharable Links
Sharable Links allow you to create secure, public URLs for files stored in your R2 buckets. Share files with anyone without requiring them to authenticate or have access to your R2 Explorer instance.

Overview
With Sharable Links, you can:
- Share files publicly - Generate a unique URL that works without authentication
- Protect with passwords - Optionally require a password to access shared files
- Set expiration dates - Configure when links automatically expire
- Limit downloads - Control how many times a file can be downloaded
- Manage all links - View and revoke active share links from the dashboard
How It Works
When you create a sharable link:
- R2 Explorer generates a unique 10-character share ID
- Share metadata is stored in your R2 bucket under
.r2-explorer/sharable-links/ - A public URL is created:
https://your-domain.com/share/{shareId} - Anyone with the link can access the file (subject to your security settings)
The actual file remains in your R2 bucket - the share link provides controlled, temporary access without moving or copying the file.
Cloudflare Access Users
If your R2 Explorer is protected by Cloudflare Access, you need to configure a bypass policy for share links to work publicly.
Setup Steps:
- Go to your Cloudflare Zero Trust dashboard
- Navigate to Access → Applications
- Create a new Self-hosted application (or edit your existing R2 Explorer application)
- In the application settings:
- Public hostname: Your R2 Explorer domain
- Path:
/share/*
- Create an access policy:
- Policy name: "Bypass for Public Shares"
- Action: Bypass
- Session duration: No session
This ensures that /share/* endpoints are publicly accessible while keeping the rest of your R2 Explorer protected by Cloudflare Access.
Creating a Sharable Link
From the Dashboard
- Right-click on any file in your R2 Explorer interface
- Select "Share File" from the context menu
- Configure your share options:
- Expiration Time (optional): Set when the link expires (in seconds)
- Password (optional): Require a password to access the file
- Max Downloads (optional): Limit how many times the file can be downloaded
- Click "Create Share Link"
- Copy the generated URL and share it
Via API
Create a share link programmatically:
curl -X POST https://your-domain.com/api/buckets/{bucket}/{base64Key}/share \
-H "Content-Type: application/json" \
-d '{
"expiresIn": 3600,
"password": "secret123",
"maxDownloads": 10
}'Response:
{
"shareId": "abc123xyz9",
"shareUrl": "https://your-domain.com/share/abc123xyz9",
"expiresAt": 1735574400000
}Share Options Explained
Expiration Time
Control how long the share link remains valid:
- Format: Time in seconds (relative to creation time)
- Example:
3600= 1 hour,86400= 24 hours - Default: No expiration (link works indefinitely)
When a link expires, anyone attempting to access it receives a 410 Gone response.
Password Protection
Add an extra layer of security:
- Format: Plain text string (automatically hashed with SHA-256)
- Access: Users must provide
?password=yourpasswordin the URL - Security: Passwords are never stored in plain text
Example usage:
https://your-domain.com/share/abc123xyz9?password=secret123Download Limits
Restrict how many times a file can be downloaded:
- Format: Integer number
- Example:
5= file can be downloaded 5 times - Behavior: After limit is reached, the link returns
403 Forbidden - Tracking: Download counter increments on each successful access
Accessing a Shared File
Without Password
Simply open the share URL in any browser:
https://your-domain.com/share/abc123xyz9With Password
Append the password as a query parameter:
https://your-domain.com/share/abc123xyz9?password=secret123The browser will download the file directly with the original filename and content type preserved.
Managing Share Links
View All Active Links
From the dashboard:
- Navigate to any bucket
- Open the Share Links Manager (icon in the toolbar)
- View all active shares for files in that bucket
Each entry shows:
- File name and path
- Share ID and URL
- Creation date
- Expiration status
- Password protection status
- Download count (if limited)
Revoke a Share Link
To revoke access to a shared file:
- Open the Share Links Manager
- Find the link you want to revoke
- Click the Delete/Revoke button
Or via API:
curl -X DELETE https://your-domain.com/api/buckets/{bucket}/share/{shareId}When a link is revoked:
- The share metadata is deleted from R2
- The public URL immediately returns
404 Not Found - The original file remains untouched in your bucket
API Reference
Create Share Link
Endpoint: POST /api/buckets/:bucket/:key/share
Request Body:
{
"expiresIn": 3600, // Optional: seconds until expiration
"password": "string", // Optional: password protection
"maxDownloads": 10 // Optional: download limit
}Response: 200 OK
{
"shareId": "abc123xyz9",
"shareUrl": "https://your-domain.com/share/abc123xyz9",
"expiresAt": 1735574400000 // Optional: timestamp in ms
}Access Shared File
Endpoint: GET /share/:shareId
Query Parameters:
password(optional): Required if share is password-protected
Response: 200 OK - File download with original content-type
Error Responses:
401 Unauthorized- Password required or incorrect403 Forbidden- Download limit exceeded404 Not Found- Share ID doesn't exist or was revoked410 Gone- Share link has expired
List Share Links
Endpoint: GET /api/buckets/:bucket/shares
Response: 200 OK
{
"shares": [
{
"shareId": "abc123xyz9",
"shareUrl": "https://your-domain.com/share/abc123xyz9",
"key": "documents/report.pdf",
"createdAt": 1735570800000,
"expiresAt": 1735574400000,
"isExpired": false,
"hasPassword": true,
"maxDownloads": 10,
"currentDownloads": 3
}
]
}Delete Share Link
Endpoint: DELETE /api/buckets/:bucket/share/:shareId
Response: 200 OK
{
"success": true
}Security Considerations
Authentication Bypass
- Share links intentionally bypass authentication to allow public access
- The
/share/:shareIdendpoint does not require Basic Auth or Cloudflare Access - Use passwords and expiration times for sensitive files
Password Security
- Passwords are hashed with SHA-256 before storage
- Never stored in plain text in your R2 bucket
- Use strong passwords for sensitive files
Storage Location
Share metadata is stored at:
.r2-explorer/sharable-links/{shareId}.jsonThis prefix is hidden from the dashboard UI by default.
Best Practices
- Use expiration times for temporary shares
- Enable password protection for sensitive files
- Set download limits to prevent abuse
- Regularly audit active share links
- Revoke unused links to minimize exposure
Common Use Cases
Temporary File Sharing
Share a file for 24 hours:
{
"expiresIn": 86400
}One-Time Download
Allow file to be downloaded only once:
{
"maxDownloads": 1
}Secure Client Delivery
Share with password and expiration:
{
"expiresIn": 3600,
"password": "client-specific-password",
"maxDownloads": 5
}Public Asset Hosting
Create permanent public links (no restrictions):
{}Troubleshooting
Link Returns 404
Possible causes:
- Share ID is incorrect
- Link was revoked/deleted
- File was deleted from R2 (but share metadata still exists)
Solution: Verify the share ID in the Share Links Manager
Link Returns 401
Possible causes:
- Password is required but not provided
- Incorrect password supplied
Solution: Include ?password=your-password in the URL
Link Returns 410
Cause: Share link has expired
Solution: Create a new share link with updated expiration time
Link Returns 403
Cause: Download limit exceeded
Solution: Either:
- Create a new share link with a higher limit
- Revoke and recreate the share to reset the counter
Integration Examples
Generate Share Link in JavaScript
async function createShareLink(bucket, fileKey, options = {}) {
const base64Key = btoa(fileKey);
const response = await fetch(
`https://your-domain.com/api/buckets/${bucket}/${base64Key}/share`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa('username:password')
},
body: JSON.stringify(options)
}
);
return await response.json();
}
// Usage
const share = await createShareLink('my-bucket', 'docs/report.pdf', {
expiresIn: 3600,
maxDownloads: 10
});
console.log('Share URL:', share.shareUrl);List All Shares in Python
import requests
import base64
def list_shares(bucket, auth_username, auth_password):
auth = base64.b64encode(
f"{auth_username}:{auth_password}".encode()
).decode()
response = requests.get(
f"https://your-domain.com/api/buckets/{bucket}/shares",
headers={"Authorization": f"Basic {auth}"}
)
return response.json()
# Usage
shares = list_shares('my-bucket', 'admin', 'secret')
for share in shares['shares']:
print(f"{share['key']}: {share['shareUrl']}")Technical Details
Share ID Generation
- 10 characters long
- Alphanumeric (a-z, A-Z, 0-9)
- Cryptographically random
- ~3.6 trillion possible combinations
Metadata Storage Format
Share metadata JSON structure:
{
"shareId": "abc123xyz9",
"bucket": "my-bucket",
"key": "documents/report.pdf",
"createdAt": 1735570800000,
"expiresAt": 1735574400000,
"passwordHash": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
"maxDownloads": 10,
"currentDownloads": 0
}Performance
- Share link lookups scan all buckets to find the share metadata
- For optimal performance with many buckets, consider organizing shares
- Metadata files are small (~500 bytes) and cached by R2
Limitations
- Share IDs must be unique across all buckets
- Maximum of 1 million share links per R2 account (R2 object limit per prefix)
- No built-in preview for shared files (direct download only)
- Download counter increments on request, not completed download
Need Help?
- 📚 Full API Documentation: API Reference
- 💬 Ask questions: GitHub Discussions
- 🐛 Report issues: GitHub Issues