cloudflare-r2

R2 is S3-compatible object storage with zero egress fees. Access via Workers Binding API or S3 API.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cloudflare-r2" with this command: npx skills add itechmeat/llm-code/itechmeat-llm-code-cloudflare-r2

Cloudflare R2

R2 is S3-compatible object storage with zero egress fees. Access via Workers Binding API or S3 API.

Quick Start

npx wrangler r2 bucket create my-bucket

// wrangler.jsonc { "r2_buckets": [{ "binding": "MY_BUCKET", "bucket_name": "my-bucket" }] }

export interface Env { MY_BUCKET: R2Bucket; }

export default { async fetch(request: Request, env: Env): Promise<Response> { const key = new URL(request.url).pathname.slice(1);

if (request.method === "PUT") {
  await env.MY_BUCKET.put(key, request.body);
  return new Response("Uploaded", { status: 201 });
}

if (request.method === "GET") {
  const object = await env.MY_BUCKET.get(key);
  if (!object) return new Response("Not Found", { status: 404 });
  const headers = new Headers();
  object.writeHttpMetadata(headers);
  headers.set("etag", object.httpEtag);
  return new Response(object.body, { headers });
}
return new Response("Method Not Allowed", { status: 405 });

}, };

Binding Configuration

{ "r2_buckets": [ { "binding": "MY_BUCKET", "bucket_name": "my-bucket", "preview_bucket_name": "my-bucket-preview", // Optional: for local dev "jurisdiction": "eu" // Optional: GDPR } ] }

See binding.md for details.

Workers Binding API (R2Bucket)

Method Description

head(key)

Get metadata without body

get(key, options?)

Get object with body

put(key, value, options?)

Store object

delete(key | keys[])

Delete up to 1000 keys

list(options?)

List objects with pagination

createMultipartUpload(key, options?)

Start multipart upload

resumeMultipartUpload(key, uploadId)

Resume multipart upload

get with range

const partial = await env.MY_BUCKET.get("large.bin", { range: { offset: 0, length: 1024 }, });

put with metadata

await env.MY_BUCKET.put("file.txt", "Hello!", { httpMetadata: { contentType: "text/plain" }, customMetadata: { author: "Alice" }, storageClass: "InfrequentAccess", // Optional });

list with pagination

const listed = await env.MY_BUCKET.list({ prefix: "images/", limit: 100 }); for (const obj of listed.objects) console.log(obj.key, obj.size); if (listed.truncated) { /* use listed.cursor for next page */ }

See api.md for complete reference.

R2Object / R2ObjectBody

interface R2Object { key: string; version: string; size: number; etag: string; httpEtag: string; uploaded: Date; httpMetadata: R2HTTPMetadata; customMetadata: Record<string, string>; storageClass: "Standard" | "InfrequentAccess"; writeHttpMetadata(headers: Headers): void; }

interface R2ObjectBody extends R2Object { body: ReadableStream; arrayBuffer(): Promise<ArrayBuffer>; text(): Promise<string>; json<T>(): Promise<T>; blob(): Promise<Blob>; }

S3 API Compatibility

Endpoint: https://<ACCOUNT_ID>.r2.cloudflarestorage.com

import { S3Client } from "@aws-sdk/client-s3";

const S3 = new S3Client({ region: "auto", endpoint: https://${ACCOUNT_ID}.r2.cloudflarestorage.com, credentials: { accessKeyId: ACCESS_KEY_ID, secretAccessKey: SECRET_ACCESS_KEY }, });

Region: Always "auto" (or "us-east-1" alias).

Presigned URLs

import { GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

const downloadUrl = await getSignedUrl(S3, new GetObjectCommand({ Bucket: "my-bucket", Key: "file.pdf" }), { expiresIn: 3600 });

const uploadUrl = await getSignedUrl(S3, new PutObjectCommand({ Bucket: "my-bucket", Key: "file.pdf", ContentType: "application/pdf" }), { expiresIn: 3600 });

Important: Presigned URLs work only with S3 endpoint (not custom domains). Configure CORS for browser use. See presigned-urls.md.

Multipart Uploads

For objects > 100 MB.

const mpu = await env.MY_BUCKET.createMultipartUpload("large.zip"); const part1 = await mpu.uploadPart(1, chunk1); // Min 5 MiB const part2 = await mpu.uploadPart(2, chunk2); await mpu.complete([part1, part2]); // or: await mpu.abort();

Resume: env.MY_BUCKET.resumeMultipartUpload(key, uploadId)

Limits: 5 MiB–5 GiB per part, 10,000 parts max, ~5 TiB max object. See multipart.md.

CORS Configuration

[ { "AllowedOrigins": ["https://example.com"], "AllowedMethods": ["GET", "PUT"], "AllowedHeaders": ["Content-Type"], "ExposeHeaders": ["ETag"], "MaxAgeSeconds": 3600 } ]

npx wrangler r2 bucket cors set my-bucket --file cors.json

Lifecycle Policies

Delete after 90 days

npx wrangler r2 bucket lifecycle add my-bucket --id "cleanup" --expire-days 90

Transition to Infrequent Access

npx wrangler r2 bucket lifecycle add my-bucket --id "archive" --transition-days 30 --transition-class STANDARD_IA

With prefix

npx wrangler r2 bucket lifecycle add my-bucket --id "logs" --prefix "logs/" --expire-days 7

Abort incomplete multipart

npx wrangler r2 bucket lifecycle add my-bucket --id "mpu" --abort-incomplete-days 1

See lifecycle.md for S3 API examples.

Storage Classes

Class Storage Notes

Standard $0.015/GB Default

InfrequentAccess $0.01/GB +$0.01/GB retrieval, 30-day min

await env.MY_BUCKET.put("archive.zip", data, { storageClass: "InfrequentAccess" });

Event Notifications

Push to Cloudflare Queues on object changes.

npx wrangler queues create r2-events npx wrangler r2 bucket notification create my-bucket --event-type object-create --queue r2-events

Events: object-create , object-delete

export default { async queue(batch: MessageBatch<R2EventMessage>, env: Env) { for (const msg of batch.messages) { console.log(${msg.body.action}: ${msg.body.object.key}); msg.ack(); } }, };

Public Buckets

Custom domain (recommended)

Dashboard → Bucket → Settings → Custom Domains → Add. Enables Cache, WAF, Access.

r2.dev (dev only)

Dashboard → Bucket → Settings → Public Development URL → Enable.

Warning: r2.dev is rate-limited. Use custom domain for production.

Data Migration

Super Slurper (bulk)

Dashboard → R2 → Data Migration. Copies from S3/GCS/compatible storage. Objects > 1 TB skipped.

Sippy (incremental)

npx wrangler r2 bucket sippy enable my-bucket --provider s3 --bucket source --access-key-id <KEY> --secret-access-key <SECRET>

Copies on-demand as objects are requested.

Wrangler Commands

Bucket

wrangler r2 bucket create|delete|list|info <name>

Object

wrangler r2 object put|get|delete <bucket>/<key> [--file <path>]

CORS

wrangler r2 bucket cors set|get|delete <bucket>

Lifecycle

wrangler r2 bucket lifecycle list|add|remove <bucket>

Notifications

wrangler r2 bucket notification list|create|delete <bucket>

Sippy

wrangler r2 bucket sippy enable|disable|get <bucket>

Limits

Parameter Limit

Buckets per account 1,000,000

Object size 5 TiB

Single upload 5 GiB

Multipart parts 10,000

Key length 1,024 bytes

Metadata size 8,192 bytes

Delete batch 1,000 keys

Lifecycle rules 1,000/bucket

Notification rules 100/bucket

Pricing

Metric Standard Infrequent Access

Storage $0.015/GB $0.01/GB

Class A $4.50/M $9.00/M

Class B $0.36/M $0.90/M

Retrieval Free $0.01/GB

Egress Free Free

Free tier: 10 GB storage, 1M Class A, 10M Class B.

Class A: PUT, COPY, LIST, CreateMultipartUpload Class B: GET, HEAD Free: DELETE, AbortMultipartUpload

See pricing.md for optimization tips.

Conditional Operations

const object = await env.MY_BUCKET.get("file.txt", { onlyIf: { etagMatches: expectedEtag }, // or: etagDoesNotMatch, uploadedBefore, uploadedAfter });

// Or from HTTP headers const object = await env.MY_BUCKET.get("file.txt", { onlyIf: request.headers });

Server-Side Encryption (SSE-C)

await env.MY_BUCKET.put("secret.txt", data, { ssecKey: encryptionKey }); const object = await env.MY_BUCKET.get("secret.txt", { ssecKey: encryptionKey });

Lost key = lost data.

Prohibitions

  • ❌ Do not use r2.dev for production

  • ❌ Do not store encryption keys in code

  • ❌ Do not skip CORS for browser access

  • ❌ Do not ignore multipart limits (5 MiB min)

  • ❌ Do not use presigned URLs with custom domains

References

  • binding.md — Binding configuration

  • api.md — Workers API reference

  • presigned-urls.md — Browser integration

  • multipart.md — Large file uploads

  • lifecycle.md — Object expiration

  • pricing.md — Cost optimization

Links

  • Documentation

  • Changelog

Cross-References

  • cloudflare-workers — Worker development

  • cloudflare-pages — Pages with R2

  • cloudflare-queues — Event consumers

  • cloudflare-d1 — SQL database

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

react-testing-library

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

social-writer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

commits

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

mantine-dev

No summary provided by upstream source.

Repository SourceNeeds Review