Features
- Private bucket: Objects only accessible via Worker API (
GET /object?key=...) - Public bucket: Objects get a public URL (e.g.
https://pub-xxx.r2.dev/key) when uploaded withpublic=true - List operations with pagination, prefix filtering, and directory-style delimiters
- Custom metadata support via
X-Custom-Metadata-*headers - Content-Type handling for serving images and other media
Setup
Create R2 buckets
In the Cloudflare dashboard → R2 → Create bucket:
- Create
r2-storage-bucket(private - default settings) - Create
r2-storage-public(for public files)
Enable public access
For the public bucket only:
- Go to R2 → select
r2-storage-public→ Settings - Under Public Development URL, click Enable
- Type
allowwhen prompted and confirm - Copy the public bucket URL (looks like
https://pub-xxxxx.r2.dev)
You can also use a custom domain instead of the
r2.dev URLConfigure wrangler.json
Update your Replace
wrangler.json with the bucket bindings:https://pub-xxxxx.r2.dev with your actual public bucket URL.API Reference
List objects
GET /list
List objects from a bucket with optional filtering and pagination.
Query Parameters
Set to
true or 1 to list from the public bucket. Omit for private bucket.Filter objects by key prefix (e.g.,
images/ to list only keys starting with “images/”)Maximum number of keys to return (1-1000)
Pagination cursor from a previous response (when
truncated: true)Delimiter for directory-style listing (e.g.,
/ to group by directory)Response
Array of object metadata
Whether there are more results available
Cursor for next page (only present when
truncated: true)Example
Get object
GET /object
Download an object from R2. Returns the raw file with appropriate Content-Type.
Query Parameters
Object key to retrieve
Set to
true or 1 to get from the public bucketHeaders
Optional byte range for partial content (e.g.,
bytes=0-1023)Response
Returns the raw object body with headers:Content-Type: From object’s HTTP metadataETag: Object version identifierContent-Length: Size in bytes
Example
Get object metadata
HEAD /object
Get object metadata without downloading the body.
Query Parameters
Object key to check
Set to
true or 1 to check the public bucketResponse
Object key
Size in bytes
Entity tag
ISO 8601 timestamp
Custom metadata if present
HTTP metadata including contentType
Example
Upload object
PUT /object
Upload an object to R2. Send raw bytes in the request body.
Query Parameters
Object key (path) for the upload
Set to
true or 1 to upload to the public bucket and get a public URLHeaders
MIME type of the file (e.g.,
image/png, application/pdf)Custom metadata headers (e.g.,
X-Custom-Metadata-Author: John)Request Body
Raw bytes of the file to upload.Response
Object key that was uploaded
Always
true on successPublic URL (only present for public bucket uploads when
PUBLIC_BUCKET_URL is configured)Example
Delete object
DELETE /object
Delete an object from R2.
Query Parameters
Object key to delete
Set to
true or 1 to delete from the public bucketResponse
Object key that was deleted
Always
true on successExample
Use Cases
Image hosting
Upload images to the public bucket and use the returned URL in your app:Private file storage
Store documents that should only be accessible through your Worker:Organized file structure
Use prefixes and delimiters for directory-like organization:Environment Variables
Bindings
Private R2 bucket binding (name:
r2-storage-bucket)Public R2 bucket binding (name:
r2-storage-public)Deploy
Configure variables
In Workers & Pages → your Worker → Settings → Variables:
- Add
PUBLIC_BUCKET_URL= your public bucket URL (e.g.,https://pub-xxxxx.r2.dev)
Error Responses
All errors return JSON with this structure:Human-readable error message
Error code:
INVALID_QUERY, NOT_FOUND, MISSING_PARAM, INTERNAL_ERRORSource Code
View the complete source code on GitHub: r2-storageCloudflare Features
- Workers: Serverless compute at the edge
- R2: Object storage with zero egress fees
- Public buckets: Direct public access to objects
- Hono: Lightweight web framework